Passed
Pull Request — master (#709)
by Julius
02:29
created

CardService::rename()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.9457
c 0
b 0
f 0
cc 6
nc 5
nop 2
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Julius Härtl <[email protected]>
4
 *
5
 * @author Julius Härtl <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 *  This program is free software: you can redistribute it and/or modify
10
 *  it under the terms of the GNU Affero General Public License as
11
 *  published by the Free Software Foundation, either version 3 of the
12
 *  License, or (at your option) any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU Affero General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU Affero General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\Deck\Service;
25
26
use OCA\Deck\Activity\ActivityManager;
27
use OCA\Deck\Activity\ChangeSet;
28
use OCA\Deck\Db\AssignedUsers;
29
use OCA\Deck\Db\AssignedUsersMapper;
30
use OCA\Deck\Db\Card;
31
use OCA\Deck\Db\CardMapper;
32
use OCA\Deck\Db\Acl;
33
use OCA\Deck\Db\ChangeHelper;
34
use OCA\Deck\Db\StackMapper;
35
use OCA\Deck\Notification\NotificationHelper;
36
use OCA\Deck\Db\BoardMapper;
37
use OCA\Deck\Db\LabelMapper;
38
use OCA\Deck\NotFoundException;
39
use OCA\Deck\StatusException;
40
use OCA\Deck\BadRequestException;
41
use OCP\Comments\ICommentsManager;
42
use OCP\IUserManager;
43
44
class CardService {
45
46
	private $cardMapper;
47
	private $stackMapper;
48
	private $boardMapper;
49
	private $labelMapper;
50
	private $permissionService;
51
	private $boardService;
52
	private $notificationHelper;
53
	private $assignedUsersMapper;
54
	private $attachmentService;
55
	private $currentUser;
56
	private $activityManager;
57
	private $commentsManager;
58
	private $changeHelper;
59
	private $userManager;
60
61
	public function __construct(
62
		CardMapper $cardMapper,
63
		StackMapper $stackMapper,
64
		BoardMapper $boardMapper,
65
		LabelMapper $labelMapper,
66
		PermissionService $permissionService,
67
		BoardService $boardService,
68
		NotificationHelper $notificationHelper,
69
		AssignedUsersMapper $assignedUsersMapper,
70
		AttachmentService $attachmentService,
71
		ActivityManager $activityManager,
72
		ICommentsManager $commentsManager,
73
		IUserManager $userManager,
74
		ChangeHelper $changeHelper,
75
		$userId
76
	) {
77
		$this->cardMapper = $cardMapper;
78
		$this->stackMapper = $stackMapper;
79
		$this->boardMapper = $boardMapper;
80
		$this->labelMapper = $labelMapper;
81
		$this->permissionService = $permissionService;
82
		$this->boardService = $boardService;
83
		$this->notificationHelper = $notificationHelper;
84
		$this->assignedUsersMapper = $assignedUsersMapper;
85
		$this->attachmentService = $attachmentService;
86
		$this->activityManager = $activityManager;
87
		$this->commentsManager = $commentsManager;
88
		$this->userManager = $userManager;
89
		$this->changeHelper = $changeHelper;
90
		$this->currentUser = $userId;
91
	}
92
93
	public function enrich($card) {
94
		$cardId = $card->getId();
95
		$card->setAssignedUsers($this->assignedUsersMapper->find($cardId));
96
		$card->setLabels($this->labelMapper->findAssignedLabelsForCard($cardId));
97
		$card->setAttachmentCount($this->attachmentService->count($cardId));
98
		$user = $this->userManager->get($this->currentUser);
99
		$lastRead = $this->commentsManager->getReadMark('deckCard', (string)$card->getId(), $user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($this->currentUser) on line 98 can be null; however, OCP\Comments\ICommentsManager::getReadMark() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
100
		$count = $this->commentsManager->getNumberOfCommentsForObject('deckCard', (string)$card->getId(), $lastRead);
101
		$card->setCommentsUnread($count);
102
	}
103
104
	public function fetchDeleted($boardId) {
105
		$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
106
		$cards = $this->cardMapper->findDeleted($boardId);
107
		foreach ($cards as $card) {
108
			$this->enrich($card);
109
		}
110
		return $cards;
111
	}
112
113
	/**
114
	 * @param $cardId
115
	 * @return \OCA\Deck\Db\RelationalEntity
116
	 * @throws \OCA\Deck\NoPermissionException
117
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
118
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
119
	 * @throws BadRequestException
120
	 */
121
	public function find($cardId) {
122
123
		if (is_numeric($cardId) === false) {
124
			throw new BadRequestException('card id must be a number');
125
		}
126
127
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
128
		$card = $this->cardMapper->find($cardId);
129
		$assignedUsers = $this->assignedUsersMapper->find($card->getId());
130
		$attachments = $this->attachmentService->findAll($cardId, true);
131
		$card->setAssignedUsers($assignedUsers);
132
		$card->setAttachments($attachments);
133
		$this->enrich($card);
134
		return $card;
135
	}
136
137
	/**
138
	 * @param $title
139
	 * @param $stackId
140
	 * @param $type
141
	 * @param integer $order
142
	 * @param $owner
143
	 * @return \OCP\AppFramework\Db\Entity
144
	 * @throws StatusException
145
	 * @throws \OCA\Deck\NoPermissionException
146
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
147
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
148
	 * @throws BadrequestException
149
	 */
150
	public function create($title, $stackId, $type, $order, $owner) {
151
152
		if ($title === 'false' || $title === null) {
153
			throw new BadRequestException('title must be provided');
154
		}
155
156
		if (is_numeric($stackId) === false) {
157
			throw new BadRequestException('stack id must be a number');
158
		}
159
160
		if ($type === 'false' || $type === null) {
161
			throw new BadRequestException('type must be provided');
162
		}
163
164
		if (is_numeric($order) === false) {
165
			throw new BadRequestException('order must be a number');
166
		}
167
168
		if ($owner === false || $owner === null) {
169
			throw new BadRequestException('owner must be provided');
170
		}
171
172
		$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
173
		if ($this->boardService->isArchived($this->stackMapper, $stackId)) {
174
			throw new StatusException('Operation not allowed. This board is archived.');
175
		}
176
		$card = new Card();
177
		$card->setTitle($title);
178
		$card->setStackId($stackId);
179
		$card->setType($type);
180
		$card->setOrder($order);
181
		$card->setOwner($owner);
182
		$card = $this->cardMapper->insert($card);
183
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_CREATE);
184
		$this->changeHelper->cardChanged($card->getId(), false);
185
		return $card;
186
	}
187
188
	/**
189
	 * @param $id
190
	 * @return \OCP\AppFramework\Db\Entity
191
	 * @throws StatusException
192
	 * @throws \OCA\Deck\NoPermissionException
193
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
194
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
195
	 * @throws BadRequestException
196
	 */
197 View Code Duplication
	public function delete($id) {
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...
198
199
		if (is_numeric($id) === false) {
200
			throw new BadRequestException('card id must be a number');
201
		}
202
203
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
204
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
205
			throw new StatusException('Operation not allowed. This board is archived.');
206
		}
207
		$card = $this->cardMapper->find($id);
208
		$card->setDeletedAt(time());
209
		$this->cardMapper->update($card);
210
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_DELETE);
211
		$this->changeHelper->cardChanged($card->getId(), false);
212
		return $card;
213
	}
214
215
	/**
216
	 * @param $id
217
	 * @param $title
218
	 * @param $stackId
219
	 * @param $type
220
	 * @param $order
221
	 * @param $description
222
	 * @param $owner
223
	 * @param $duedate
224
	 * @return \OCP\AppFramework\Db\Entity
225
	 * @throws StatusException
226
	 * @throws \OCA\Deck\NoPermissionException
227
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
228
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
229
	 * @throws BadRequestException
230
	 */
231
	public function update($id, $title, $stackId, $type, $order = 0, $description = '', $owner, $duedate = null, $deletedAt) {
232
233
		if (is_numeric($id) === false) {
234
			throw new BadRequestException('card id must be a number');
235
		}
236
237
		if ($title === false || $title === null) {
238
			throw new BadRequestException('title must be provided');
239
		}
240
241
		if (is_numeric($stackId) === false) {
242
			throw new BadRequestException('stack id must be a number $$$');
243
		}
244
245
		if ($type === false || $type === null) {
246
			throw new BadRequestException('type must be provided');
247
		}
248
249
		if ($owner === false || $owner === null) {
250
			throw new BadRequestException('owner must be provided');
251
		}
252
253
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
254
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
255
			throw new StatusException('Operation not allowed. This board is archived.');
256
		}
257
		$card = $this->cardMapper->find($id);
258
		if ($card->getArchived()) {
259
			throw new StatusException('Operation not allowed. This card is archived.');
260
		}
261
		$changes = new ChangeSet($card);
262
		if ($card->getLastEditor() !== $this->currentUser && $card->getLastEditor() !== null) {
263
			$this->activityManager->triggerEvent(
264
				ActivityManager::DECK_OBJECT_CARD,
265
				$card,
266
				ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION,
267
				[
268
					'before' => $card->getDescriptionPrev(),
269
					'after' => $card->getDescription()
270
				],
271
				$card->getLastEditor()
272
			);
273
274
			$card->setDescriptionPrev($card->getDescription());
275
			$card->setLastEditor($this->currentUser);
276
		}
277
		$card->setTitle($title);
278
		$card->setStackId($stackId);
279
		$card->setType($type);
280
		$card->setOrder($order);
281
		$card->setOwner($owner);
282
		$card->setDuedate($duedate);
283
		$card->setDeletedAt($deletedAt);
284
285
		// Trigger update events before setting description as it is handled separately
286
		$changes->setAfter($card);
287
		$this->activityManager->triggerUpdateEvents(ActivityManager::DECK_OBJECT_CARD, $changes, ActivityManager::SUBJECT_CARD_UPDATE);
288
289
		if ($card->getDescriptionPrev() === null) {
290
			$card->setDescriptionPrev($card->getDescription());
291
		}
292
		$card->setDescription($description);
293
294
295
		$card = $this->cardMapper->update($card);
296
		$this->changeHelper->cardChanged($card->getId(), true);
297
		return $card;
298
	}
299
300
	/**
301
	 * @param $id
302
	 * @param $title
303
	 * @return \OCP\AppFramework\Db\Entity
304
	 * @throws StatusException
305
	 * @throws \OCA\Deck\NoPermissionException
306
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
307
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
308
	 * @throws BadRequestException
309
	 */
310
	public function rename($id, $title) {
311
312
		if (is_numeric($id) === false) {
313
			throw new BadRequestException('id must be a number');
314
		}
315
316
		if ($title === false || $title === null) {
317
			throw new BadRequestException('title must be provided');
318
		}
319
320
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
321
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
322
			throw new StatusException('Operation not allowed. This board is archived.');
323
		}
324
		$card = $this->cardMapper->find($id);
325
		if ($card->getArchived()) {
326
			throw new StatusException('Operation not allowed. This card is archived.');
327
		}
328
		$card->setTitle($title);
329
		$this->changeHelper->cardChanged($card->getId(), false);
330
		return $this->cardMapper->update($card);
331
	}
332
333
	/**
334
	 * @param $id
335
	 * @param $stackId
336
	 * @param $order
337
	 * @return array
338
	 * @throws StatusException
339
	 * @throws \OCA\Deck\NoPermissionException
340
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
341
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
342
	 * @throws BadRequestException
343
	 */
344
	public function reorder($id, $stackId, $order) {
345
346
		if (is_numeric($id) === false) {
347
			throw new BadRequestException('card id must be a number');
348
		}
349
350
		if (is_numeric($stackId) === false) {
351
			throw new BadRequestException('stack id must be a number');
352
		}
353
354
		if (is_numeric($order) === false) {
355
			throw new BadRequestException('order must be a number');
356
		}
357
358
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
359
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
360
			throw new StatusException('Operation not allowed. This board is archived.');
361
		}
362
		$cards = $this->cardMapper->findAll($stackId);
363
		$result = [];
364
		$i = 0;
365
		foreach ($cards as $card) {
366
			if ($card->getArchived()) {
367
				throw new StatusException('Operation not allowed. This card is archived.');
368
			}
369
			if ($card->id === $id) {
370
				$card->setOrder($order);
371
				$card->setLastModified(time());
372
			}
373
374
			if ($i === $order) {
375
				$i++;
376
			}
377
378
			if ($card->id !== $id) {
379
				$card->setOrder($i++);
380
			}
381
			$this->cardMapper->update($card);
382
			$result[$card->getOrder()] = $card;
383
		}
384
		$this->changeHelper->cardChanged($id, false);
385
		return $result;
386
	}
387
388
	/**
389
	 * @param $id
390
	 * @return \OCP\AppFramework\Db\Entity
391
	 * @throws StatusException
392
	 * @throws \OCA\Deck\NoPermissionException
393
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
394
	 * @throws \OCP\AppFramework\Db\
395
	 * @throws BadRequestException
396
	 */
397 View Code Duplication
	public function archive($id) {
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...
398
399
		if (is_numeric($id) === false) {
400
			throw new BadRequestException('id must be a number');
401
		}
402
403
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
404
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
405
			throw new StatusException('Operation not allowed. This board is archived.');
406
		}
407
		$card = $this->cardMapper->find($id);
408
		$card->setArchived(true);
409
		$newCard = $this->cardMapper->update($card);
410
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_ARCHIVE);
411
		$this->changeHelper->cardChanged($id, false);
412
		return $newCard;
413
	}
414
415
	/**
416
	 * @param $id
417
	 * @return \OCP\AppFramework\Db\Entity
418
	 * @throws StatusException
419
	 * @throws \OCA\Deck\NoPermissionException
420
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
421
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
422
	 * @throws BadRequestException
423
	 */
424 View Code Duplication
	public function unarchive($id) {
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...
425
426
		if (is_numeric($id) === false) {
427
			throw new BadRequestException('id must be a number');
428
		}
429
430
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
431
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
432
			throw new StatusException('Operation not allowed. This board is archived.');
433
		}
434
		$card = $this->cardMapper->find($id);
435
		$card->setArchived(false);
436
		$newCard = $this->cardMapper->update($card);
437
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_UNARCHIVE);
438
		$this->changeHelper->cardChanged($id, false);
439
		return $newCard;
440
	}
441
442
	/**
443
	 * @param $cardId
444
	 * @param $labelId
445
	 * @throws StatusException
446
	 * @throws \OCA\Deck\NoPermissionException
447
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
448
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
449
	 * @throws BadRequestException
450
	 */
451 View Code Duplication
	public function assignLabel($cardId, $labelId) {
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...
452
453
		if (is_numeric($cardId) === false) {
454
			throw new BadRequestException('card id must be a number');
455
		}
456
457
		if (is_numeric($labelId) === false) {
458
			throw new BadRequestException('label id must be a number');
459
		}
460
461
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
462
		if ($this->boardService->isArchived($this->cardMapper, $cardId)) {
463
			throw new StatusException('Operation not allowed. This board is archived.');
464
		}
465
		$card = $this->cardMapper->find($cardId);
466
		if ($card->getArchived()) {
467
			throw new StatusException('Operation not allowed. This card is archived.');
468
		}
469
		$label = $this->labelMapper->find($labelId);
470
		$this->cardMapper->assignLabel($cardId, $labelId);
471
		$this->changeHelper->cardChanged($cardId, false);
472
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]);
473
	}
474
475
	/**
476
	 * @param $cardId
477
	 * @param $labelId
478
	 * @throws StatusException
479
	 * @throws \OCA\Deck\NoPermissionException
480
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
481
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
482
	 * @throws BadRequestException
483
	 */
484 View Code Duplication
	public function removeLabel($cardId, $labelId) {
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...
485
486
		if (is_numeric($cardId) === false) {
487
			throw new BadRequestException('card id must be a number');
488
		}
489
490
		if (is_numeric($labelId) === false) {
491
			throw new BadRequestException('label id must be a number');
492
		}
493
494
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
495
		if ($this->boardService->isArchived($this->cardMapper, $cardId)) {
496
			throw new StatusException('Operation not allowed. This board is archived.');
497
		}
498
		$card = $this->cardMapper->find($cardId);
499
		if ($card->getArchived()) {
500
			throw new StatusException('Operation not allowed. This card is archived.');
501
		}
502
		$label = $this->labelMapper->find($labelId);
503
		$this->cardMapper->removeLabel($cardId, $labelId);
504
		$this->changeHelper->cardChanged($cardId, false);
505
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]);
506
	}
507
508
	/**
509
	 * @param $cardId
510
	 * @param $userId
511
	 * @return bool|null|\OCP\AppFramework\Db\Entity
512
	 * @throws BadRequestException
513
	 * @throws \OCA\Deck\NoPermissionException
514
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
515
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
516
	 */
517
	public function assignUser($cardId, $userId) {
518
519
		if (is_numeric($cardId) === false) {
520
			throw new BadRequestException('card id must be a number');
521
		}
522
523
		if ($userId === false || $userId === null) {
524
			throw new BadRequestException('user id must be provided');
525
		}
526
527
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
528
		$assignments = $this->assignedUsersMapper->find($cardId);
529
		foreach ($assignments as $assignment) {
0 ignored issues
show
Bug introduced by
The expression $assignments of type array|object<OCP\AppFramework\Db\Entity> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
530
			if ($assignment->getParticipant() === $userId) {
531
				return false;
532
			}
533
		}
534
		$card = $this->cardMapper->find($cardId);
535
536
		if ($userId !== $this->currentUser) {
537
			/* Notifyuser about the card assignment */
538
			$this->notificationHelper->sendCardAssigned($card, $userId);
539
		}
540
541
		$assignment = new AssignedUsers();
542
		$assignment->setCardId($cardId);
543
		$assignment->setParticipant($userId);
544
		$assignment = $this->assignedUsersMapper->insert($assignment);
545
		$this->changeHelper->cardChanged($cardId, false);
546
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_ASSIGN, ['assigneduser' => $userId]);
547
		return $assignment;
548
	}
549
550
	/**
551
	 * @param $cardId
552
	 * @param $userId
553
	 * @return \OCP\AppFramework\Db\Entity
554
	 * @throws BadRequestException
555
	 * @throws NotFoundException
556
	 * @throws \OCA\Deck\NoPermissionException
557
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
558
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
559
	 */
560
	public function unassignUser($cardId, $userId) {
561
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
562
563
		if (is_numeric($cardId) === false) {
564
			throw new BadRequestException('card id must be a number');
565
		}
566
567
		if ($userId === false || $userId === null) {
568
			throw new BadRequestException('user must be provided');
569
		}
570
571
		$assignments = $this->assignedUsersMapper->find($cardId);
572
		foreach ($assignments as $assignment) {
0 ignored issues
show
Bug introduced by
The expression $assignments of type array|object<OCP\AppFramework\Db\Entity> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
573
			if ($assignment->getParticipant() === $userId) {
574
				$assignment = $this->assignedUsersMapper->delete($assignment);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\AppFramework\Db\Mapper::delete() has been deprecated with message: 14.0.0 Move over to QBMapper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
575
				$card = $this->cardMapper->find($cardId);
576
				$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_UNASSIGN, ['assigneduser' => $userId]);
577
				$this->changeHelper->cardChanged($cardId, false);
578
				return $assignment;
579
			}
580
		}
581
		throw new NotFoundException('No assignment for ' . $userId . 'found.');
582
	}
583
}
584