Completed
Push — federated-circles ( 80cab6...74a72b )
by Maxence
02:32
created

CirclesMapper::getDetailsFromCircleByName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
cc 2
eloc 14
nc 2
nop 1
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
					'You cannot use the Circles Application until your administrator has allowed at least one type of circles'
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 111 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
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")
337
			);
338
		}
339
340
		return $result[0];
341
	}
342
343
344
	/**
345
	 * @param $circleName
346
	 *
347
	 * @return Circle|null
348
	 */
349
	public function getDetailsFromCircleByName($circleName) {
350
		$qb = $this->isCircleUniqueSql();
351
		$expr = $qb->expr();
352
353
		$qb->andWhere($expr->iLike('c.name', $qb->createNamedParameter($circleName)));
354
		$qb->andWhere($expr->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL)));
355
356
		$cursor = $qb->execute();
357
		$data = $cursor->fetch();
358
		$cursor->closeCursor();
359
360
		if ($data === false) {
361
			return null;
362
		}
363
364
		$circle = new Circle();
0 ignored issues
show
Bug introduced by
The call to Circle::__construct() misses a required argument $l10n.

This check looks for function calls that miss required arguments.

Loading history...
365
		$circle->setId($data['id']);
366
		$circle->setType($data['type']);
367
368
		return $circle;
369
	}
370
371
372
	/**
373
	 * @param Circle $circle
374
	 * @param Member $owner
375
	 *
376
	 * @return bool
377
	 * @throws CircleAlreadyExistsException
378
	 */
379
	public function create(
380
		Circle & $circle, Member & $owner
381
	) {
382
383
		if (!$this->isCircleUnique($circle, $owner)) {
384
			throw new CircleAlreadyExistsException(
385
				$this->l10n->t('A circle with that name exists')
386
			);
387
		}
388
389
		$qb = $this->db->getQueryBuilder();
390
		$qb->insert(self::TABLENAME)
391
		   ->setValue('name', $qb->createNamedParameter($circle->getName()))
392
		   ->setValue('description', $qb->createNamedParameter($circle->getDescription()))
393
		   ->setValue('type', $qb->createNamedParameter($circle->getType()))
394
		   ->setValue('creation', $qb->createFunction('NOW()'));
395
396
		$qb->execute();
397
		$circleId = $qb->getLastInsertId();
398
399
		$circle->setId($circleId);
400
		$owner->setLevel(Member::LEVEL_OWNER)
401
			  ->setStatus(Member::STATUS_MEMBER)
402
			  ->setCircleId($circleId);
403
404
		return true;
405
	}
406
407
408
	/**
409
	 * remove a circle
410
	 *
411
	 * @param int $circleId
412
	 *
413
	 * @internal param Circle $circle
414
	 */
415 View Code Duplication
	public function destroy(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
416
		$circleId
417
	) {
418
		$qb = $this->db->getQueryBuilder();
419
		$qb->delete(self::TABLENAME)
420
		   ->where(
421
			   $qb->expr()
422
				  ->eq(
423
					  'id', $qb->createNamedParameter($circleId)
424
				  )
425
		   );
426
427
		$qb->execute();
428
	}
429
430
431
	/**
432
	 * returns if the circle is already in database
433
	 *
434
	 * @param Circle $circle
435
	 * @param Member $owner
436
	 *
437
	 * @return bool
438
	 */
439
	public function isCircleUnique(
440
		Circle $circle, Member $owner
441
	) {
442
443
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
444
			return $this->isPersonalCircleUnique($circle, $owner);
445
		}
446
447
		$qb = $this->isCircleUniqueSql();
448
		$cursor = $qb->execute();
449
450
		while ($data = $cursor->fetch()) {
451
			if (strtolower($data['name']) === strtolower($circle->getName())) {
452
				return false;
453
			}
454
		}
455
		$cursor->closeCursor();
456
457
		return true;
458
	}
459
460
461
	/**
462
	 * Return SQL for isCircleUnique();
463
	 *
464
	 * @return IQueryBuilder
465
	 */
466
	private function isCircleUniqueSql() {
467
		$qb = $this->db->getQueryBuilder();
468
469
		/** @noinspection PhpMethodParametersCountMismatchInspection */
470
		$qb->select(
471
			'c.id', 'c.name', 'c.type'
472
		)
473
		   ->from(self::TABLENAME, 'c')
474
		   ->where(
475
			   $qb->expr()
476
				  ->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL))
477
		   );
478
479
		return $qb;
480
	}
481
482
483
	/**
484
	 * return if the personal circle is unique
485
	 *
486
	 * @param Circle $circle
487
	 * @param Member $owner
488
	 *
489
	 * @return bool
490
	 */
491
	private function isPersonalCircleUnique(
492
		Circle $circle, Member $owner
493
	) {
494
495
		$list = $this->findCirclesByUser(
496
			$owner->getUserId(), Circle::CIRCLES_PERSONAL, $circle->getName(),
497
			Member::LEVEL_OWNER
498
		);
499
500
		foreach ($list as $test) {
501
			if ($test->getName() === $circle->getName()) {
502
				return false;
503
			}
504
		}
505
506
		return true;
507
	}
508
}
509
510