Completed
Push — master ( 0a2d75...d5a533 )
by Maxence
03:06
created

CirclesMapper::isCircleUnique()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
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 string */
46
	private $userId;
47
48
	/** @var L10N */
49
	private $l10n;
50
51
	/** @var MiscService */
52
	private $miscService;
53
54
	public function __construct($userId, IDBConnection $db, $l10n, $miscService) {
55
		parent::__construct($db, self::TABLENAME, 'OCA\Circles\Db\Circles');
56
		$this->userId = $userId;
57
		$this->l10n = $l10n;
58
		$this->miscService = $miscService;
59
	}
60
61
62
	/**
63
	 * Returns all circle from a user point-of-view
64
	 *
65
	 * @param $userId
66
	 * @param $type
67
	 * @param string $name
68
	 * @param int $level
69
	 * @param int $circleId
70
	 *
71
	 * @return Circle[]
72
	 * @throws ConfigNoCircleAvailable
73
	 */
74
	public function findCirclesByUser($userId, $type, $name = '', $level = 0, $circleId = -1) {
75
76
		$type = (int)$type;
77
		$level = (int)$level;
78
		$circleId = (int)$circleId;
79
		$qb = $this->findCirclesByUserSql($userId, $type, $name, $level, $circleId);
80
		$cursor = $qb->execute();
81
82
		$result = [];
83
		while ($data = $cursor->fetch()) {
84
			if ($name === '' || stripos($data['name'], $name) !== false) {
85
				$circle = Circle::fromArray2($this->l10n, $data);
86
				$this->fillCircleUserIdAndOwner($circle, $data);
87
				$result[] = $circle;
88
			}
89
		}
90
		$cursor->closeCursor();
91
92
		return $result;
93
	}
94
95
96
	/**
97
	 * @param Circle $circle
98
	 * @param array $data
99
	 */
100
	private function fillCircleUserIdAndOwner(Circle &$circle, array $data) {
101
		$owner = new Member($this->l10n);
102
		$owner->setUserId($data['owner_id']);
103
		$circle->setOwner($owner);
104
105
		$user = new Member($this->l10n);
106
		$user->setUserId($this->userId);
107
		$user->setStatus($data['status']);
108
		$user->setLevel($data['level']);
109
		$user->setJoined($data['joined']);
110
		$circle->setUser($user);
111
	}
112
113
	/**
114
	 * Returns SQL for findCirclesByUser
115
	 *
116
	 * @param $userId
117
	 * @param $type
118
	 * @param $name
119
	 * @param $level
120
	 * @param $circleId
121
	 *
122
	 * @return IQueryBuilder
123
	 * @throws ConfigNoCircleAvailable
124
	 */
125
	private function findCirclesByUserSql($userId, $type, $name, $level, $circleId) {
126
		$qb = $this->db->getQueryBuilder();
127
		$expr = $qb->expr();
128
129
		/** @noinspection PhpMethodParametersCountMismatchInspection */
130
		$qb->select(
131
			'c.id', 'c.unique_id', 'c.name', 'c.description', 'c.settings', 'c.type', 'c.creation',
132
			'u.joined', 'u.level', 'u.status'
133
		)
134
		   ->selectAlias('o.user_id', 'owner_id')
135
		   ->from(self::TABLENAME, 'c')
136
		   ->from(MembersMapper::TABLENAME, 'o')
137
		   ->where(
138
			   $expr->eq('c.id', 'o.circle_id'),
139
			   $expr->eq('o.level', $qb->createNamedParameter(Member::LEVEL_OWNER))
140
		   )
141
		   ->leftJoin(
142
			   'c', MembersMapper::TABLENAME, 'u',
143
			   $expr->andX(
144
				   $expr->eq('c.id', 'u.circle_id'),
145
				   $expr->eq('u.user_id', $qb->createNamedParameter($userId))
146
			   )
147
		   );
148
149
		$this->buildWithMemberLevel($qb, 'u.level', $level);
150
		$this->buildWithCircleId($qb, 'c.id', $circleId);
151
		$this->buildWithOrXTypes($qb, $userId, $type, $name, $circleId);
152
153
		//	$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...
154
		$qb->orderBy('c.name', 'ASC');
155
156
		return $qb;
157
	}
158
159
160
	/**
161
	 * @param IQueryBuilder $qb
162
	 * @param $userId
163
	 * @param $type
164
	 * @param $name
165
	 * @param $circleId
166
	 *
167
	 * @throws ConfigNoCircleAvailable
168
	 */
169
	private function buildWithOrXTypes(&$qb, $userId, $type, $name, $circleId) {
170
171
		$orTypesArray = $this->fillOrXTypes($qb, $userId, $type, $name, $circleId);
172
		if (sizeof($orTypesArray) === 0) {
173
			throw new ConfigNoCircleAvailable(
174
				$this->l10n->t(
175
					'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...
176
				)
177
			);
178
		}
179
180
		$orXTypes = $qb->expr()
181
					   ->orX();
182
183
		foreach ($orTypesArray as $orTypes) {
184
			$orXTypes->add($orTypes);
185
		}
186
187
		$qb->andWhere($orXTypes);
188
	}
189
190
191
	/**
192
	 * fill with sql conditions for each type of circles.
193
	 *
194
	 * @param $qb
195
	 * @param $userId
196
	 * @param $type
197
	 * @param $name
198
	 * @param $circleId
199
	 *
200
	 * @return array
201
	 */
202
	private function fillOrXTypes(&$qb, $userId, $type, $name, $circleId) {
203
204
		$orTypesArray = [];
205
		array_push($orTypesArray, $this->generateTypeEntryForCirclePersonal($qb, $type, $userId));
206
		array_push(
207
			$orTypesArray, $this->generateTypeEntryForCircleHidden($qb, $type, $circleId, $name)
208
		);
209
		array_push($orTypesArray, $this->generateTypeEntryForCirclePrivate($qb, $type));
210
		array_push($orTypesArray, $this->generateTypeEntryForCirclePublic($qb, $type));
211
212
		return array_filter($orTypesArray);
213
	}
214
215
	/**
216
	 * @param IQueryBuilder $qb
217
	 * @param string $field
218
	 * @param int $circleId
219
	 */
220
	private function buildWithCircleId(IQueryBuilder & $qb, $field, $circleId) {
221
		if ($circleId > 0) {
222
			$qb->andWhere(
223
				$qb->expr()
224
				   ->eq((string)$field, $qb->createNamedParameter((int)$circleId))
225
			);
226
		}
227
	}
228
229
230
	/**
231
	 * @param IQueryBuilder $qb
232
	 * @param string $field
233
	 * @param int $level
234
	 */
235
	private function buildWithMemberLevel(IQueryBuilder & $qb, $field, $level) {
236
		if ($level > 0) {
237
			$qb->andWhere(
238
				$qb->expr()
239
				   ->gte((string)$field, $qb->createNamedParameter((int)$level))
240
			);
241
		}
242
	}
243
244
245
	/**
246
	 * @param IQueryBuilder $qb
247
	 * @param int $type
248
	 * @param int|string $userId
249
	 *
250
	 * @return \OCP\DB\QueryBuilder\ICompositeExpression
251
	 */
252
	private function generateTypeEntryForCirclePersonal(IQueryBuilder $qb, $type, $userId
253
	) {
254
		if (Circle::CIRCLES_PERSONAL & (int)$type) {
255
			$expr = $qb->expr();
256
257
			/** @noinspection PhpMethodParametersCountMismatchInspection */
258
			return $qb->expr()
259
					  ->andX(
260
						  $expr->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL)),
261
						  $expr->eq('o.user_id', $qb->createNamedParameter((string)$userId))
262
263
					  );
264
		}
265
266
		return null;
267
	}
268
269
	/**
270
	 * @param IQueryBuilder $qb
271
	 * @param int $type
272
	 * @param int $circleId
273
	 * @param string $name
274
	 *
275
	 * @return string
276
	 */
277
	private function generateTypeEntryForCircleHidden(IQueryBuilder $qb, $type, $circleId, $name) {
278
		if (!(Circle::CIRCLES_HIDDEN & (int)$type)) {
279
			return null;
280
		}
281
282
		$expr = $qb->expr();
283
		/** @noinspection PhpMethodParametersCountMismatchInspection */
284
		$sqb = $expr->andX(
285
			$expr->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_HIDDEN)),
286
			$expr->orX(
287
				$expr->gte(
288
					'u.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)
289
				),
290
				$expr->eq('c.id', $qb->createNamedParameter((string)$circleId)),
291
				$expr->eq('c.name', $qb->createNamedParameter((string)$name))
292
			)
293
		);
294
295
		return $sqb;
296
	}
297
298
299
	/**
300
	 * @param IQueryBuilder $qb
301
	 * @param int $type
302
	 *
303
	 * @return string
304
	 */
305 View Code Duplication
	private function generateTypeEntryForCirclePrivate(IQueryBuilder $qb, $type) {
306
		if (Circle::CIRCLES_PRIVATE & (int)$type) {
307
			return $qb->expr()
308
					  ->eq(
309
						  'c.type',
310
						  $qb->createNamedParameter(Circle::CIRCLES_PRIVATE)
311
					  );
312
		}
313
314
		return null;
315
	}
316
317
318
	/**
319
	 * @param IQueryBuilder $qb
320
	 * @param int $type
321
	 *
322
	 * @return string
323
	 */
324 View Code Duplication
	private function generateTypeEntryForCirclePublic(IQueryBuilder $qb, $type) {
325
		if (Circle::CIRCLES_PUBLIC & (int)$type) {
326
			return $qb->expr()
327
					  ->eq(
328
						  'c.type',
329
						  $qb->createNamedParameter(Circle::CIRCLES_PUBLIC)
330
					  );
331
		}
332
333
		return null;
334
	}
335
336
	/**
337
	 * Returns details about a circle.
338
	 *
339
	 * @param string $userId
340
	 * @param int $circleId
341
	 *
342
	 * @return Circle
343
	 * @throws CircleDoesNotExistException
344
	 * @throws ConfigNoCircleAvailable
345
	 */
346
	public function getDetailsFromCircle($circleId, $userId) {
347
348
		try {
349
			$result = $this->findCirclesByUser($userId, Circle::CIRCLES_ALL, '', 0, $circleId);
350
		} catch (ConfigNoCircleAvailable $e) {
351
			throw $e;
352
		}
353
354
		if (sizeof($result) !== 1) {
355
			throw new CircleDoesNotExistException(
356
				$this->l10n->t("The circle does not exist or is hidden")
357
			);
358
		}
359
360
		return $result[0];
361
	}
362
363
364
	/**
365
	 * @param $circleName
366
	 *
367
	 * @return Circle|null
368
	 */
369
	public function getDetailsFromCircleByName($circleName) {
370
		$qb = $this->isCircleUniqueSql();
371
		$expr = $qb->expr();
372
373
		$qb->andWhere($expr->iLike('c.name', $qb->createNamedParameter($circleName)));
374
		$qb->andWhere($expr->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL)));
375
376
		$cursor = $qb->execute();
377
		$data = $cursor->fetch();
378
		$cursor->closeCursor();
379
380
		if ($data === false) {
381
			return null;
382
		}
383
384
		$circle = new Circle($this->l10n);
385
		$circle->setId($data['id']);
386
		$circle->setName($data['name']);
387
		$circle->setType($data['type']);
388
		$circle->setUniqueId($data['unique_id']);
389
		$circle->setSettings($data['settings']);
390
391
		return $circle;
392
	}
393
394
395
	/**
396
	 * @param Circle $circle
397
	 *
398
	 * @return bool
399
	 * @throws CircleAlreadyExistsException
400
	 */
401
	public function create(Circle & $circle) {
402
403
		if (!$this->isCircleUnique($circle)) {
404
			throw new CircleAlreadyExistsException(
405
				$this->l10n->t('A circle with that name exists')
406
			);
407
		}
408
409
		$circle->generateUniqueId();
410
		$qb = $this->db->getQueryBuilder();
411
		$qb->insert(self::TABLENAME)
412
		   ->setValue('unique_id', $qb->createNamedParameter($circle->getUniqueId(true)))
413
		   ->setValue('name', $qb->createNamedParameter($circle->getName()))
414
		   ->setValue('description', $qb->createNamedParameter($circle->getDescription()))
415
		   ->setValue('settings', $qb->createNamedParameter($circle->getSettings(true)))
416
		   ->setValue('type', $qb->createNamedParameter($circle->getType()))
417
		   ->setValue('creation', $qb->createFunction('NOW()'));
418
419
		$qb->execute();
420
		$circleId = $qb->getLastInsertId();
421
422
		$circle->setId($circleId);
423
424
		return true;
425
	}
426
427
428
	/**
429
	 * remove a circle
430
	 *
431
	 * @param int $circleId
432
	 *
433
	 * @internal param Circle $circle
434
	 */
435 View Code Duplication
	public function destroy($circleId) {
436
		$qb = $this->db->getQueryBuilder();
437
		$qb->delete(self::TABLENAME)
438
		   ->where(
439
			   $qb->expr()
440
				  ->eq(
441
					  'id', $qb->createNamedParameter($circleId)
442
				  )
443
		   );
444
445
		$qb->execute();
446
	}
447
448
449
	/**
450
	 * returns if the circle is already in database
451
	 *
452
	 * @param Circle $circle
453
	 *
454
	 * @return bool
455
	 */
456
	public function isCircleUnique(Circle $circle) {
457
458
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
459
			return $this->isPersonalCircleUnique($circle);
460
		}
461
462
		$qb = $this->isCircleUniqueSql();
463
		$cursor = $qb->execute();
464
465
		while ($data = $cursor->fetch()) {
466
			if (strtolower($data['name']) === strtolower($circle->getName())) {
467
				return false;
468
			}
469
		}
470
		$cursor->closeCursor();
471
472
		return true;
473
	}
474
475
476
	/**
477
	 * Return SQL for isCircleUnique();
478
	 *
479
	 * @return IQueryBuilder
480
	 */
481
	private function isCircleUniqueSql() {
482
		$qb = $this->db->getQueryBuilder();
483
484
		/** @noinspection PhpMethodParametersCountMismatchInspection */
485
		$qb->select(
486
			'c.id', 'c.unique_id', 'c.name', 'c.type', 'c.settings'
487
		)
488
		   ->from(self::TABLENAME, 'c')
489
		   ->where(
490
			   $qb->expr()
491
				  ->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL))
492
		   );
493
494
		return $qb;
495
	}
496
497
498
	/**
499
	 * return if the personal circle is unique
500
	 *
501
	 * @param Circle $circle
502
	 *
503
	 * @return bool
504
	 */
505
	private function isPersonalCircleUnique(Circle $circle) {
506
507
		$list = $this->findCirclesByUser(
508
			$circle->getOwner()
509
				   ->getUserId(), Circle::CIRCLES_PERSONAL, $circle->getName(),
510
			Member::LEVEL_OWNER
511
		);
512
513
		foreach ($list as $test) {
514
			if ($test->getName() === $circle->getName()) {
515
				return false;
516
			}
517
		}
518
519
		return true;
520
	}
521
}
522
523