Completed
Push — some-scrutinizing ( 29b6ab...e5bac5 )
by Maxence
18:16
created

CirclesMapper::findCirclesByUserSql()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 23
nc 1
nop 5
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 OCA\Circles\Exceptions\CircleAlreadyExistsException;
30
use OCA\Circles\Exceptions\CircleDoesNotExistException;
31
use OCA\Circles\Exceptions\ConfigNoCircleAvailable;
32
use OCA\Circles\Model\Circle;
33
use OCA\Circles\Model\Member;
34
35
use OCP\DB\QueryBuilder\IQueryBuilder;
36
use OCP\IDBConnection;
37
use OCP\AppFramework\Db\Mapper;
38
39
class CirclesMapper extends Mapper {
40
41
	const TABLENAME = 'circles_circles';
42
43
	private $miscService;
44
45
	public function __construct(IDBConnection $db, $miscService) {
46
		parent::__construct($db, self::TABLENAME, 'OCA\Circles\Db\Circles');
47
		$this->miscService = $miscService;
48
49
	}
50
51
52
	/**
53
	 * Returns all circle from a user point-of-view
54
	 *
55
	 * @param $userId
56
	 * @param $type
57
	 * @param string $name
58
	 * @param int $level
59
	 * @param int $circleId
60
	 *
61
	 * @return Circle[]
62
	 * @throws ConfigNoCircleAvailable
63
	 */
64
	public function findCirclesByUser($userId, $type, $name = '', $level = 0, $circleId = -1) {
65
66
		$type = (int)$type;
67
		$level = (int)$level;
68
		$circleId = (int)$circleId;
69
		$qb = $this->findCirclesByUserSql($userId, $type, $name, $level, $circleId);
70
		$cursor = $qb->execute();
71
72
		$result = [];
73
		while ($data = $cursor->fetch()) {
74
			if ($name === '' || stripos($data['name'], $name) !== false) {
75
				$circle = new Circle();
76
				$result[] = $circle->fromArray($data);
77
			}
78
		}
79
		$cursor->closeCursor();
80
81
		return $result;
82
	}
83
84
85
	/**
86
	 * Returns SQL for findCirclesByUser
87
	 *
88
	 * @param $userId
89
	 * @param $type
90
	 * @param $name
91
	 * @param $level
92
	 * @param $circleId
93
	 *
94
	 * @return IQueryBuilder
95
	 * @throws ConfigNoCircleAvailable
96
	 */
97
	private function findCirclesByUserSql($userId, $type, $name, $level, $circleId) {
98
		$qb = $this->db->getQueryBuilder();
99
		$expr = $qb->expr();
100
101
		/** @noinspection PhpMethodParametersCountMismatchInspection */
102
		$qb->select(
103
			'c.id', 'c.name', 'c.description', 'c.type', 'c.creation',
104
			'u.joined', 'u.level', 'u.status'
105
		)
106
		   ->selectAlias('o.user_id', 'owner')
107
		   ->from(self::TABLENAME, 'c')
108
		   ->from(MembersMapper::TABLENAME, 'o')
109
		   ->where(
110
			   $expr->eq('c.id', 'o.circle_id'),
111
			   $expr->eq('o.level', $qb->createNamedParameter(Member::LEVEL_OWNER))
112
		   )
113
		   ->leftJoin(
114
			   'c', MembersMapper::TABLENAME, 'u',
115
			   $expr->andX(
116
				   $expr->eq('c.id', 'u.circle_id'),
117
				   $expr->eq('u.user_id', $qb->createNamedParameter($userId))
118
			   )
119
		   );
120
121
		$this->buildWithMemberLevel($qb, 'u.level', $level);
122
		$this->buildWithCircleId($qb, 'c.id', $circleId);
123
		$this->buildWithOrXTypes($qb, $userId, $type, $name, $circleId);
124
125
		$qb->groupBy('c.id');
126
		$qb->orderBy('c.name', 'ASC');
127
128
		return $qb;
129
	}
130
131
132
	private function buildWithOrXTypes(&$qb, $userId, $type, $name, $circleId) {
133
134
		$orTypesArray = [];
135
		array_push($orTypesArray, $this->generateTypeEntryForCirclePersonal($qb, $type, $userId));
136
		array_push(
137
			$orTypesArray, $this->generateTypeEntryForCircleHidden($qb, $type, $circleId, $name)
138
		);
139
		array_push($orTypesArray, $this->generateTypeEntryForCirclePrivate($qb, $type));
140
		array_push($orTypesArray, $this->generateTypeEntryForCirclePublic($qb, $type));
141
142
		$orTypesArray = array_filter($orTypesArray);
143
144
		if (sizeof($orTypesArray) === 0) {
145
			throw new ConfigNoCircleAvailable();
146
		}
147
148
		$orXTypes = $qb->expr()
149
					   ->orX();
150
151
		foreach ($orTypesArray as $orTypes) {
152
			$orXTypes->add($orTypes);
153
		}
154
155
		$qb->andWhere($orXTypes);
156
	}
157
158
	/**
159
	 * @param IQueryBuilder $qb
160
	 * @param string $field
161
	 * @param int $circleId
162
	 */
163
	private function buildWithCircleId(IQueryBuilder & $qb, string $field, int $circleId) {
164
		if ($circleId > 0) {
165
			$qb->andWhere(
166
				$qb->expr()
167
				   ->eq($field, $qb->createNamedParameter($circleId))
168
			);
169
		}
170
	}
171
172
173
	/**
174
	 * @param IQueryBuilder $qb
175
	 * @param string $field
176
	 * @param int $level
177
	 */
178
	private function buildWithMemberLevel(IQueryBuilder & $qb, string $field, int $level) {
179
		if ($level > 0) {
180
			$qb->andWhere(
181
				$qb->expr()
182
				   ->gte($field, $qb->createNamedParameter($level))
183
			);
184
		}
185
	}
186
187
188
	/**
189
	 * @param IQueryBuilder $qb
190
	 * @param int $type
191
	 * @param int|string $userId
192
	 *
193
	 * @return \OCP\DB\QueryBuilder\ICompositeExpression
194
	 */
195
	private function generateTypeEntryForCirclePersonal(IQueryBuilder $qb, int $type, string $userId
196
	) {
197
		if (Circle::CIRCLES_PERSONAL & (int)$type) {
198
199
			/** @noinspection PhpMethodParametersCountMismatchInspection */
200
			return $qb->expr()
201
					  ->andX(
202
						  $qb->expr()
203
							 ->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL)),
204
						  $qb->expr()
205
							 ->eq('o.user_id', $qb->createNamedParameter($userId))
206
207
					  );
208
		}
209
210
		return null;
211
	}
212
213
	/**
214
	 * @param IQueryBuilder $qb
215
	 * @param int $type
216
	 * @param int $circleId
217
	 * @param string $name
218
	 *
219
	 * @return string
220
	 */
221
	private function generateTypeEntryForCircleHidden(
222
		IQueryBuilder $qb, int $type, int $circleId, string $name
223
	) {
224
		if (!(Circle::CIRCLES_HIDDEN & (int)$type)) {
225
			return null;
226
		}
227
228
		$expr = $qb->expr();
229
		/** @noinspection PhpMethodParametersCountMismatchInspection */
230
		$sqb = $expr->andX(
231
			$expr->eq('c.type', $qb->createNamedParameter(Circle::CIRCLES_HIDDEN)),
232
			$expr->orX(
233
				$expr->gte(
234
					'u.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)
235
				),
236
				$expr->eq('c.id', $qb->createNamedParameter($circleId)),
237
				$expr->eq('c.name', $qb->createNamedParameter($name))
238
			)
239
		);
240
241
		return $sqb;
242
	}
243
244
245
	/**
246
	 * @param IQueryBuilder $qb
247
	 * @param int $type
248
	 *
249
	 * @return string
250
	 */
251 View Code Duplication
	private function generateTypeEntryForCirclePrivate(IQueryBuilder $qb, int $type) {
252
		if (Circle::CIRCLES_PRIVATE & (int)$type) {
253
			return $qb->expr()
254
					  ->eq(
255
						  'c.type',
256
						  $qb->createNamedParameter(Circle::CIRCLES_PRIVATE)
257
					  );
258
		}
259
260
		return null;
261
	}
262
263
264
	/**
265
	 * @param IQueryBuilder $qb
266
	 * @param int $type
267
	 *
268
	 * @return string
269
	 */
270 View Code Duplication
	private function generateTypeEntryForCirclePublic(IQueryBuilder $qb, int $type) {
271
		if (Circle::CIRCLES_PUBLIC & (int)$type) {
272
			return $qb->expr()
273
					  ->eq(
274
						  'c.type',
275
						  $qb->createNamedParameter(Circle::CIRCLES_PUBLIC)
276
					  );
277
		}
278
279
		return null;
280
	}
281
282
	/**
283
	 * Returns details about a circle.
284
	 *
285
	 * @param string $userId
286
	 * @param int $circleId
287
	 *
288
	 * @return Circle
289
	 * @throws CircleDoesNotExistException
290
	 * @throws ConfigNoCircleAvailable
291
	 */
292
	public function getDetailsFromCircle($userId, $circleId) {
293
294
		try {
295
			$result = $this->findCirclesByUser($userId, Circle::CIRCLES_ALL, '', 0, $circleId);
296
		} catch (ConfigNoCircleAvailable $e) {
297
			throw $e;
298
		}
299
300
		if (sizeof($result) !== 1) {
301
			throw new CircleDoesNotExistException(
302
				"The circle does not exist or is hidden to the user"
303
			);
304
		}
305
306
		return $result[0];
307
	}
308
309
310
	/**
311
	 * @param Circle $circle
312
	 * @param Member $owner
313
	 *
314
	 * @return bool
315
	 * @throws CircleAlreadyExistsException
316
	 */
317
	public function create(Circle & $circle, Member & $owner) {
318
319
		if (!$this->isCircleUnique($circle, $owner)) {
320
			throw new CircleAlreadyExistsException();
321
		}
322
323
		$qb = $this->db->getQueryBuilder();
324
		$qb->insert(self::TABLENAME)
325
		   ->setValue('name', $qb->createNamedParameter($circle->getName()))
326
		   ->setValue('description', $qb->createNamedParameter($circle->getDescription()))
327
		   ->setValue('type', $qb->createNamedParameter($circle->getType()));
328
		$qb->execute();
329
		$circleId = $qb->getLastInsertId();
330
331
		$circle->setId($circleId);
332
		$owner->setLevel(Member::LEVEL_OWNER)
333
			  ->setStatus(Member::STATUS_MEMBER)
334
			  ->setCircleId($circleId);
335
336
		return true;
337
	}
338
339
340
	/**
341
	 * remove a circle
342
	 *
343
	 * @param Circle $circle
344
	 */
345 View Code Duplication
	public function destroy(Circle $circle) {
346
		$qb = $this->db->getQueryBuilder();
347
		$qb->delete(self::TABLENAME)
348
		   ->where(
349
			   $qb->expr()
350
				  ->eq(
351
					  'id', $qb->createNamedParameter($circle->getId())
352
				  )
353
		   );
354
355
		$qb->execute();
356
	}
357
358
359
	/**
360
	 * returns if the circle is already in database
361
	 *
362
	 * @param Circle $circle
363
	 * @param Member $owner
364
	 *
365
	 * @return bool
366
	 */
367
	public function isCircleUnique(Circle $circle, Member $owner) {
368
369
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
370
			return $this->isPersonalCircleUnique($circle, $owner);
371
		}
372
373
		$qb = $this->isCircleUniqueSql();
374
		$cursor = $qb->execute();
375
376
		while ($data = $cursor->fetch()) {
377
			if (strtolower($data['name']) === strtolower($circle->getName())) {
378
				return false;
379
			}
380
		}
381
		$cursor->closeCursor();
382
383
		return true;
384
	}
385
386
387
	/**
388
	 * Return SQL for isCircleUnique();
389
	 *
390
	 * @return IQueryBuilder
391
	 */
392
	private function isCircleUniqueSql() {
393
		$qb = $this->db->getQueryBuilder();
394
395
		/** @noinspection PhpMethodParametersCountMismatchInspection */
396
		$qb->select(
397
			'c.id', 'c.name', 'c.type'
398
		)
399
		   ->from(self::TABLENAME, 'c')
400
		   ->where(
401
			   $qb->expr()
402
				  ->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL))
403
		   );
404
405
		return $qb;
406
	}
407
408
409
	/**
410
	 * return if the personal circle is unique
411
	 *
412
	 * @param Circle $circle
413
	 * @param Member $owner
414
	 *
415
	 * @return bool
416
	 */
417
	private function isPersonalCircleUnique(Circle $circle, Member $owner) {
418
419
		$list = $this->findCirclesByUser(
420
			$owner->getUserId(), Circle::CIRCLES_PERSONAL, $circle->getName(),
421
			Member::LEVEL_OWNER
422
		);
423
424
		foreach ($list as $test) {
425
			if ($test->getName() === $circle->getName()) {
426
				return false;
427
			}
428
		}
429
430
		return true;
431
	}
432
}
433
434