Completed
Push — testscrut1 ( 5550f6...5e8804 )
by Maxence
02:23
created

CirclesMapper::buildWithCircleId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 5
nc 2
nop 3
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 $qb->expr()
209
					  ->andX(
210
						  $qb->expr()
211
							 ->eq(
212
								 'c.type',
213
								 $qb->createNamedParameter(Circle::CIRCLES_HIDDEN)
214
							 ),
215
						  $qb->expr()
216
							 ->orX(
217
								 $qb->expr()
218
									->gte(
219
										'u.level',
220
										$qb->createNamedParameter(Member::LEVEL_MEMBER)
221
									),
222
								 $qb->expr()
223
									->eq(
224
										'c.id',
225
										$qb->createNamedParameter($circleId)
226
									),
227
								 $qb->expr()
228
									->eq(
229
										'c.name',
230
										$qb->createNamedParameter($name)
231
									)
232
							 )
233
					  );
234
		}
235
236
		return null;
237
	}
238
239
240
	/**
241
	 * @param IQueryBuilder $qb
242
	 * @param int $type
243
	 *
244
	 * @return string
245
	 */
246 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...
247
248
		if (Circle::CIRCLES_PRIVATE & (int)$type) {
249
			return $qb->expr()
250
					  ->eq(
251
						  'c.type',
252
						  $qb->createNamedParameter(Circle::CIRCLES_PRIVATE)
253
					  );
254
		}
255
256
		return null;
257
	}
258
259
260
	/**
261
	 * @param IQueryBuilder $qb
262
	 * @param int $type
263
	 *
264
	 * @return string
265
	 */
266 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...
267
		if (Circle::CIRCLES_PUBLIC & (int)$type) {
268
			return $qb->expr()
269
					  ->eq(
270
						  'c.type',
271
						  $qb->createNamedParameter(Circle::CIRCLES_PUBLIC)
272
					  );
273
		}
274
275
		return null;
276
	}
277
278
	/**
279
	 * Returns details about a circle.
280
	 *
281
	 * @param string $userId
282
	 * @param int $circleId
283
	 *
284
	 * @return Circle
285
	 * @throws CircleDoesNotExistException
286
	 * @throws ConfigNoCircleAvailable
287
	 */
288
	public function getDetailsFromCircle($userId, $circleId) {
289
290
		try {
291
			$result = $this->findCirclesByUser($userId, Circle::CIRCLES_ALL, '', 0, $circleId);
292
		} catch (ConfigNoCircleAvailable $e) {
293
			throw $e;
294
		}
295
296
		if (sizeof($result) !== 1) {
297
			throw new CircleDoesNotExistException(
298
				"The circle does not exist or is hidden to the user"
299
			);
300
		}
301
302
		return $result[0];
303
	}
304
305
306
	/**
307
	 * @param Circle $circle
308
	 * @param Member $owner
309
	 *
310
	 * @return bool
311
	 * @throws CircleAlreadyExistsException
312
	 * @throws CircleCreationException
313
	 * @throws ConfigNoCircleAvailable
314
	 */
315
	public function create(Circle &$circle, Member &$owner) {
316
317
		if (!$this->isCircleUnique($circle, $owner)) {
318
			throw new CircleAlreadyExistsException();
319
		}
320
321
		$qb = $this->db->getQueryBuilder();
322
		$qb->insert(self::TABLENAME)
323
		   ->setValue('name', $qb->createNamedParameter($circle->getName()))
324
		   ->setValue('description', $qb->createNamedParameter($circle->getDescription()))
325
		   ->setValue('type', $qb->createNamedParameter($circle->getType()))
326
		   ->setValue('creation', 'CURRENT_TIMESTAMP()');
327
		$qb->execute();
328
		$circleId = $qb->getLastInsertId();
329
330
331
		if ($circleId < 1) {
332
			throw new CircleCreationException();
333
		}
334
335
		$circle->setId($circleId);
336
		$owner->setLevel(Member::LEVEL_OWNER)
337
			  ->setStatus(Member::STATUS_MEMBER)
338
			  ->setCircleId($circleId);
339
340
		return true;
341
	}
342
343
344
	/**
345
	 * remove a circle
346
	 *
347
	 * @param Circle $circle
348
	 */
349 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...
350
		$qb = $this->db->getQueryBuilder();
351
		$qb->delete(self::TABLENAME)
352
		   ->where(
353
			   $qb->expr()
354
				  ->eq(
355
					  'id', $qb->createNamedParameter($circle->getId())
356
				  )
357
		   );
358
359
		$qb->execute();
360
	}
361
362
363
	/**
364
	 * returns is the circle is already in database
365
	 *
366
	 * @param Circle $circle
367
	 * @param Member $owner
368
	 *
369
	 * @return bool
370
	 */
371
	public function isCircleUnique(Circle $circle, Member $owner) {
372
373
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
374
375
			$list = $this->findCirclesByUser(
376
				$owner->getUserId(), Circle::CIRCLES_PERSONAL, $circle->getName(),
377
				Member::LEVEL_OWNER
378
			);
379
380
			foreach ($list as $test) {
381
				if ($test->getName() === $circle->getName()) {
382
					return false;
383
				}
384
			}
385
386
		} else {
387
388
			$qb = $this->db->getQueryBuilder();
389
			$qb->select(
390
				'c.id', 'c.name', 'c.type'
391
			)
392
			   ->from(self::TABLENAME, 'c')
393
			   ->where(
394
				   $qb->expr()
395
					  ->neq('c.type', $qb->createNamedParameter(Circle::CIRCLES_PERSONAL))
396
			   );
397
398
			$cursor = $qb->execute();
399
400
			while ($data = $cursor->fetch()) {
401
				if (strtolower($data['name']) === strtolower($circle->getName())) {
402
					return false;
403
				}
404
			}
405
			$cursor->closeCursor();
406
407
		}
408
409
		return true;
410
	}
411
}
412
413