Completed
Push — testscrut1 ( 5e8804...78769b )
by Maxence
02:27
created

CirclesMapper::isPersonalCircleUnique()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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