Completed
Pull Request — master (#55)
by Maxence
02:47
created

CirclesMapper::findCirclesByUserSql()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 22
nc 1
nop 5
dl 0
loc 33
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Circles\Db;
28
29
use OC\L10N\L10N;
30
use OCA\Circles\Exceptions\CircleAlreadyExistsException;
31
use OCA\Circles\Exceptions\CircleDoesNotExistException;
32
use OCA\Circles\Exceptions\ConfigNoCircleAvailable;
33
use OCA\Circles\Model\Circle;
34
use OCA\Circles\Model\Member;
35
36
use OCA\Circles\Service\MiscService;
37
use OCP\DB\QueryBuilder\IQueryBuilder;
38
use OCP\IDBConnection;
39
use OCP\AppFramework\Db\Mapper;
40
41
class CirclesMapper extends Mapper {
42
43
	const TABLENAME = 'circles_circles';
44
45
	/** @var L10N */
46
	private $l10n;
47
48
	/** @var MiscService */
49
	private $miscService;
50
51
	public function __construct(IDBConnection $db, $l10n, $miscService) {
52
		parent::__construct($db, self::TABLENAME, 'OCA\Circles\Db\Circles');
53
		$this->l10n = $l10n;
54
		$this->miscService = $miscService;
55
	}
56
57
58
	/**
59
	 * Returns all circle from a user point-of-view
60
	 *
61
	 * @param $userId
62
	 * @param $type
63
	 * @param string $name
64
	 * @param int $level
65
	 * @param int $circleId
66
	 *
67
	 * @return Circle[]
68
	 * @throws ConfigNoCircleAvailable
69
	 */
70
	public function findCirclesByUser($userId, $type, $name = '', $level = 0, $circleId = -1) {
71
72
		$type = (int)$type;
73
		$level = (int)$level;
74
		$circleId = (int)$circleId;
75
		$qb = $this->findCirclesByUserSql($userId, $type, $name, $level, $circleId);
76
		$cursor = $qb->execute();
77
78
		$result = [];
79
		while ($data = $cursor->fetch()) {
80
			if ($name === '' || stripos($data['name'], $name) !== false) {
81
				$circle = new Circle($this->l10n);
82
				$result[] = $circle->fromArray($data);
83
			}
84
		}
85
		$cursor->closeCursor();
86
87
		return $result;
88
	}
89
90
91
	/**
92
	 * Returns SQL for findCirclesByUser
93
	 *
94
	 * @param $userId
95
	 * @param $type
96
	 * @param $name
97
	 * @param $level
98
	 * @param $circleId
99
	 *
100
	 * @return IQueryBuilder
101
	 * @throws ConfigNoCircleAvailable
102
	 */
103
	private function findCirclesByUserSql($userId, $type, $name, $level, $circleId) {
104
		$qb = $this->db->getQueryBuilder();
105
		$expr = $qb->expr();
106
107
		/** @noinspection PhpMethodParametersCountMismatchInspection */
108
		$qb->select(
109
			'c.id', 'c.name', 'c.description', 'c.type', 'c.creation',
110
			'u.joined', 'u.level', 'u.status'
111
		)
112
		   ->selectAlias('o.user_id', 'owner')
113
		   ->from(self::TABLENAME, 'c')
114
		   ->from(MembersMapper::TABLENAME, 'o')
115
		   ->where(
116
			   $expr->eq('c.id', 'o.circle_id'),
117
			   $expr->eq('o.level', $qb->createNamedParameter(Member::LEVEL_OWNER))
118
		   )
119
		   ->leftJoin(
120
			   'c', MembersMapper::TABLENAME, 'u',
121
			   $expr->andX(
122
				   $expr->eq('c.id', 'u.circle_id'),
123
				   $expr->eq('u.user_id', $qb->createNamedParameter($userId))
124
			   )
125
		   );
126
127
		$this->buildWithMemberLevel($qb, 'u.level', $level);
128
		$this->buildWithCircleId($qb, 'c.id', $circleId);
129
		$this->buildWithOrXTypes($qb, $userId, $type, $name, $circleId);
130
131
		//	$qb->groupBy('c.id');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
132
		$qb->orderBy('c.name', 'ASC');
133
134
		return $qb;
135
	}
136
137
138
	/**
139
	 * @param IQueryBuilder $qb
140
	 * @param $userId
141
	 * @param $type
142
	 * @param $name
143
	 * @param $circleId
144
	 *
145
	 * @throws ConfigNoCircleAvailable
146
	 */
147
	private function buildWithOrXTypes(&$qb, $userId, $type, $name, $circleId) {
148
149
		$orTypesArray = $this->fillOrXTypes($qb, $userId, $type, $name, $circleId);
150
		if (sizeof($orTypesArray) === 0) {
151
			throw new ConfigNoCircleAvailable(
152
				$this->l10n->t(
153
					'No type of circle are selected in the global configuration of the app'
154
				)
155
			);
156
		}
157
158
		$orXTypes = $qb->expr()
159
					   ->orX();
160
161
		foreach ($orTypesArray as $orTypes) {
162
			$orXTypes->add($orTypes);
163
		}
164
165
		$qb->andWhere($orXTypes);
166
	}
167
168
169
	/**
170
	 * fill with sql conditions for each type of circles.
171
	 *
172
	 * @param $qb
173
	 * @param $userId
174
	 * @param $type
175
	 * @param $name
176
	 * @param $circleId
177
	 *
178
	 * @return array
179
	 */
180
	private function fillOrXTypes(&$qb, $userId, $type, $name, $circleId) {
181
182
		$orTypesArray = [];
183
		array_push($orTypesArray, $this->generateTypeEntryForCirclePersonal($qb, $type, $userId));
184
		array_push(
185
			$orTypesArray, $this->generateTypeEntryForCircleHidden($qb, $type, $circleId, $name)
186
		);
187
		array_push($orTypesArray, $this->generateTypeEntryForCirclePrivate($qb, $type));
188
		array_push($orTypesArray, $this->generateTypeEntryForCirclePublic($qb, $type));
189
190
		return array_filter($orTypesArray);
191
	}
192
193
	/**
194
	 * @param IQueryBuilder $qb
195
	 * @param string $field
196
	 * @param int $circleId
197
	 */
198
	private function buildWithCircleId(IQueryBuilder & $qb, string $field, int $circleId) {
199
		if ($circleId > 0) {
200
			$qb->andWhere(
201
				$qb->expr()
202
				   ->eq($field, $qb->createNamedParameter($circleId))
203
			);
204
		}
205
	}
206
207
208
	/**
209
	 * @param IQueryBuilder $qb
210
	 * @param string $field
211
	 * @param int $level
212
	 */
213
	private function buildWithMemberLevel(IQueryBuilder & $qb, string $field, int $level) {
214
		if ($level > 0) {
215
			$qb->andWhere(
216
				$qb->expr()
217
				   ->gte($field, $qb->createNamedParameter($level))
218
			);
219
		}
220
	}
221
222
223
	/**
224
	 * @param IQueryBuilder $qb
225
	 * @param int $type
226
	 * @param int|string $userId
227
	 *
228
	 * @return \OCP\DB\QueryBuilder\ICompositeExpression
229
	 */
230
	private function generateTypeEntryForCirclePersonal(IQueryBuilder $qb, int $type, string $userId
231
	) {
232
		if (Circle::CIRCLES_PERSONAL & (int)$type) {
233
			$expr = $qb->expr();
234
235
			/** @noinspection PhpMethodParametersCountMismatchInspection */
236
			return $qb->expr()
237
					  ->andX(
238
						  $expr->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL)),
239
						  $expr->eq('o.user_id', $qb->createNamedParameter($userId))
240
241
					  );
242
		}
243
244
		return null;
245
	}
246
247
	/**
248
	 * @param IQueryBuilder $qb
249
	 * @param int $type
250
	 * @param int $circleId
251
	 * @param string $name
252
	 *
253
	 * @return string
254
	 */
255
	private function generateTypeEntryForCircleHidden(
256
		IQueryBuilder $qb, int $type, int $circleId, string $name
257
	) {
258
		if (!(Circle::CIRCLES_HIDDEN & (int)$type)) {
259
			return null;
260
		}
261
262
		$expr = $qb->expr();
263
		/** @noinspection PhpMethodParametersCountMismatchInspection */
264
		$sqb = $expr->andX(
265
			$expr->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_HIDDEN)),
266
			$expr->orX(
267
				$expr->gte(
268
					'u.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)
269
				),
270
				$expr->eq('c.id', $qb->createNamedParameter($circleId)),
271
				$expr->eq('c.name', $qb->createNamedParameter($name))
272
			)
273
		);
274
275
		return $sqb;
276
	}
277
278
279
	/**
280
	 * @param IQueryBuilder $qb
281
	 * @param int $type
282
	 *
283
	 * @return string
284
	 */
285 View Code Duplication
	private function generateTypeEntryForCirclePrivate(IQueryBuilder $qb, int $type) {
286
		if (Circle::CIRCLES_PRIVATE & (int)$type) {
287
			return $qb->expr()
288
					  ->eq(
289
						  'c.type',
290
						  $qb->createNamedParameter(Circle::CIRCLES_PRIVATE)
291
					  );
292
		}
293
294
		return null;
295
	}
296
297
298
	/**
299
	 * @param IQueryBuilder $qb
300
	 * @param int $type
301
	 *
302
	 * @return string
303
	 */
304 View Code Duplication
	private function generateTypeEntryForCirclePublic(IQueryBuilder $qb, int $type) {
305
		if (Circle::CIRCLES_PUBLIC & (int)$type) {
306
			return $qb->expr()
307
					  ->eq(
308
						  'c.type',
309
						  $qb->createNamedParameter(Circle::CIRCLES_PUBLIC)
310
					  );
311
		}
312
313
		return null;
314
	}
315
316
	/**
317
	 * Returns details about a circle.
318
	 *
319
	 * @param string $userId
320
	 * @param int $circleId
321
	 *
322
	 * @return Circle
323
	 * @throws CircleDoesNotExistException
324
	 * @throws ConfigNoCircleAvailable
325
	 */
326
	public function getDetailsFromCircle($circleId, $userId) {
327
328
		try {
329
			$result = $this->findCirclesByUser($userId, Circle::CIRCLES_ALL, '', 0, $circleId);
330
		} catch (ConfigNoCircleAvailable $e) {
331
			throw $e;
332
		}
333
334
		if (sizeof($result) !== 1) {
335
			throw new CircleDoesNotExistException(
336
				$this->l10n->t("The circle does not exist or is hidden to the user")
337
			);
338
		}
339
340
		return $result[0];
341
	}
342
343
344
	/**
345
	 * @param Circle $circle
346
	 * @param Member $owner
347
	 *
348
	 * @return bool
349
	 * @throws CircleAlreadyExistsException
350
	 */
351
	public function create(Circle & $circle, Member & $owner) {
352
353
		if (!$this->isCircleUnique($circle, $owner)) {
354
			throw new CircleAlreadyExistsException(
355
				$this->l10n->t('A circle with that name already exist')
356
			);
357
		}
358
359
		$qb = $this->db->getQueryBuilder();
360
		$qb->insert(self::TABLENAME)
361
		   ->setValue('name', $qb->createNamedParameter($circle->getName()))
362
		   ->setValue('description', $qb->createNamedParameter($circle->getDescription()))
363
		   ->setValue('type', $qb->createNamedParameter($circle->getType()))
364
		   ->setValue('creation', $qb->createFunction('NOW()'));
365
366
		$qb->execute();
367
		$circleId = $qb->getLastInsertId();
368
369
		$circle->setId($circleId);
370
		$owner->setLevel(Member::LEVEL_OWNER)
371
			  ->setStatus(Member::STATUS_MEMBER)
372
			  ->setCircleId($circleId);
373
374
		return true;
375
	}
376
377
378
	/**
379
	 * remove a circle
380
	 *
381
	 * @param int $circleId
382
	 *
383
	 * @internal param Circle $circle
384
	 */
385 View Code Duplication
	public function destroy($circleId) {
386
		$qb = $this->db->getQueryBuilder();
387
		$qb->delete(self::TABLENAME)
388
		   ->where(
389
			   $qb->expr()
390
				  ->eq(
391
					  'id', $qb->createNamedParameter($circleId)
392
				  )
393
		   );
394
395
		$qb->execute();
396
	}
397
398
399
	/**
400
	 * returns if the circle is already in database
401
	 *
402
	 * @param Circle $circle
403
	 * @param Member $owner
404
	 *
405
	 * @return bool
406
	 */
407
	public function isCircleUnique(Circle $circle, Member $owner) {
408
409
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
410
			return $this->isPersonalCircleUnique($circle, $owner);
411
		}
412
413
		$qb = $this->isCircleUniqueSql();
414
		$cursor = $qb->execute();
415
416
		while ($data = $cursor->fetch()) {
417
			if (strtolower($data['name']) === strtolower($circle->getName())) {
418
				return false;
419
			}
420
		}
421
		$cursor->closeCursor();
422
423
		return true;
424
	}
425
426
427
	/**
428
	 * Return SQL for isCircleUnique();
429
	 *
430
	 * @return IQueryBuilder
431
	 */
432
	private function isCircleUniqueSql() {
433
		$qb = $this->db->getQueryBuilder();
434
435
		/** @noinspection PhpMethodParametersCountMismatchInspection */
436
		$qb->select(
437
			'c.id', 'c.name', 'c.type'
438
		)
439
		   ->from(self::TABLENAME, 'c')
440
		   ->where(
441
			   $qb->expr()
442
				  ->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL))
443
		   );
444
445
		return $qb;
446
	}
447
448
449
	/**
450
	 * return if the personal circle is unique
451
	 *
452
	 * @param Circle $circle
453
	 * @param Member $owner
454
	 *
455
	 * @return bool
456
	 */
457
	private function isPersonalCircleUnique(Circle $circle, Member $owner) {
458
459
		$list = $this->findCirclesByUser(
460
			$owner->getUserId(), Circle::CIRCLES_PERSONAL, $circle->getName(),
461
			Member::LEVEL_OWNER
462
		);
463
464
		foreach ($list as $test) {
465
			if ($test->getName() === $circle->getName()) {
466
				return false;
467
			}
468
		}
469
470
		return true;
471
	}
472
}
473
474