Completed
Push — master ( 0ee0a5...f5691e )
by Julius
23s queued 10s
created

CardService::unassignUser()   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\StackMapper;
34
use OCA\Deck\Notification\NotificationHelper;
35
use OCA\Deck\Db\BoardMapper;
36
use OCA\Deck\Db\LabelMapper;
37
use OCA\Deck\NotFoundException;
38
use OCA\Deck\StatusException;
39
use OCA\Deck\BadRequestException;
40
41
class CardService {
42
43
	private $cardMapper;
44
	private $stackMapper;
45
	private $boardMapper;
46
	private $labelMapper;
47
	private $permissionService;
48
	private $boardService;
49
	private $notificationHelper;
50
	private $assignedUsersMapper;
51
	private $attachmentService;
52
	private $currentUser;
53
	private $activityManager;
54
55
	public function __construct(
56
		CardMapper $cardMapper,
57
		StackMapper $stackMapper,
58
		BoardMapper $boardMapper,
59
		LabelMapper $labelMapper,
60
		PermissionService $permissionService,
61
		BoardService $boardService,
62
		NotificationHelper $notificationHelper,
63
		AssignedUsersMapper $assignedUsersMapper,
64
		AttachmentService $attachmentService,
65
		ActivityManager $activityManager,
66
		$userId
67
	) {
68
		$this->cardMapper = $cardMapper;
69
		$this->stackMapper = $stackMapper;
70
		$this->boardMapper = $boardMapper;
71
		$this->labelMapper = $labelMapper;
72
		$this->permissionService = $permissionService;
73
		$this->boardService = $boardService;
74
		$this->notificationHelper = $notificationHelper;
75
		$this->assignedUsersMapper = $assignedUsersMapper;
76
		$this->attachmentService = $attachmentService;
77
		$this->activityManager = $activityManager;
78
		$this->currentUser = $userId;
79
	}
80
81
	public function enrich($card) {
82
		$cardId = $card->getId();
83
		$card->setAssignedUsers($this->assignedUsersMapper->find($cardId));
84
		$card->setLabels($this->labelMapper->findAssignedLabelsForCard($cardId));
85
		$card->setAttachmentCount($this->attachmentService->count($cardId));
86
	}
87
88
	public function fetchDeleted($boardId) {
89
		$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
90
		$cards = $this->cardMapper->findDeleted($boardId);
91
		foreach ($cards as $card) {
92
			$this->enrich($card);
93
		}
94
		return $cards;
95
	}
96
97
	/**
98
	 * @param $cardId
99
	 * @return \OCA\Deck\Db\RelationalEntity
100
	 * @throws \OCA\Deck\NoPermissionException
101
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
102
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
103
	 * @throws BadRequestException
104
	 */
105
	public function find($cardId) {
106
107
		if (is_numeric($cardId) === false) {
108
			throw new BadRequestException('card id must be a number');
109
		}
110
111
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
112
		$card = $this->cardMapper->find($cardId);
113
		$assignedUsers = $this->assignedUsersMapper->find($card->getId());
114
		$attachments = $this->attachmentService->findAll($cardId, true);
115
		$card->setAssignedUsers($assignedUsers);
116
		$card->setAttachments($attachments);
117
		return $card;
118
	}
119
120
	/**
121
	 * @param $title
122
	 * @param $stackId
123
	 * @param $type
124
	 * @param integer $order
125
	 * @param $owner
126
	 * @return \OCP\AppFramework\Db\Entity
127
	 * @throws StatusException
128
	 * @throws \OCA\Deck\NoPermissionException
129
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
130
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
131
	 * @throws BadrequestException
132
	 */
133
	public function create($title, $stackId, $type, $order, $owner) {
134
135
		if ($title === 'false' || $title === null) {
136
			throw new BadRequestException('title must be provided');
137
		}
138
139
		if (is_numeric($stackId) === false) {
140
			throw new BadRequestException('stack id must be a number');
141
		}
142
143
		if ($type === 'false' || $type === null) {
144
			throw new BadRequestException('type must be provided');
145
		}
146
147
		if (is_numeric($order) === false) {
148
			throw new BadRequestException('order must be a number');
149
		}
150
151
		if ($owner === false || $owner === null) {
152
			throw new BadRequestException('owner must be provided');
153
		}
154
155
		$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
156
		if ($this->boardService->isArchived($this->stackMapper, $stackId)) {
157
			throw new StatusException('Operation not allowed. This board is archived.');
158
		}
159
		$card = new Card();
160
		$card->setTitle($title);
161
		$card->setStackId($stackId);
162
		$card->setType($type);
163
		$card->setOrder($order);
164
		$card->setOwner($owner);
165
		$card = $this->cardMapper->insert($card);
166
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_CREATE);
167
		return $card;
168
	}
169
170
	/**
171
	 * @param $id
172
	 * @return \OCP\AppFramework\Db\Entity
173
	 * @throws StatusException
174
	 * @throws \OCA\Deck\NoPermissionException
175
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
176
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
177
	 * @throws BadRequestException
178
	 */
179 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...
180
181
		if (is_numeric($id) === false) {
182
			throw new BadRequestException('card id must be a number');
183
		}
184
185
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
186
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
187
			throw new StatusException('Operation not allowed. This board is archived.');
188
		}
189
		$card = $this->cardMapper->find($id);
190
		$card->setDeletedAt(time());
191
		$this->cardMapper->update($card);
192
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_DELETE);
193
		return $card;
194
	}
195
196
	/**
197
	 * @param $id
198
	 * @param $title
199
	 * @param $stackId
200
	 * @param $type
201
	 * @param $order
202
	 * @param $description
203
	 * @param $owner
204
	 * @param $duedate
205
	 * @return \OCP\AppFramework\Db\Entity
206
	 * @throws StatusException
207
	 * @throws \OCA\Deck\NoPermissionException
208
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
209
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
210
	 * @throws BadRequestException
211
	 */
212
	public function update($id, $title, $stackId, $type, $order = 0, $description = '', $owner, $duedate = null, $deletedAt) {
213
214
		if (is_numeric($id) === false) {
215
			throw new BadRequestException('card id must be a number');
216
		}
217
218
		if ($title === false || $title === null) {
219
			throw new BadRequestException('title must be provided');
220
		}
221
222
		if (is_numeric($stackId) === false) {
223
			throw new BadRequestException('stack id must be a number $$$');
224
		}
225
226
		if ($type === false || $type === null) {
227
			throw new BadRequestException('type must be provided');
228
		}
229
230
		if ($owner === false || $owner === null) {
231
			throw new BadRequestException('owner must be provided');
232
		}
233
234
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
235
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
236
			throw new StatusException('Operation not allowed. This board is archived.');
237
		}
238
		$card = $this->cardMapper->find($id);
239
		if ($card->getArchived()) {
240
			throw new StatusException('Operation not allowed. This card is archived.');
241
		}
242
		$changes = new ChangeSet($card);
243
		$card->setTitle($title);
244
		$card->setStackId($stackId);
245
		$card->setType($type);
246
		$card->setOrder($order);
247
		$card->setOwner($owner);
248
		$card->setDescription($description);
249
		$card->setDuedate($duedate);
250
		$card->setDeletedAt($deletedAt);
251
		$changes->setAfter($card);
252
		$card = $this->cardMapper->update($card);
253
		$this->activityManager->triggerUpdateEvents(ActivityManager::DECK_OBJECT_CARD, $changes, ActivityManager::SUBJECT_CARD_UPDATE);
254
		return $card;
255
	}
256
257
	/**
258
	 * @param $id
259
	 * @param $title
260
	 * @return \OCP\AppFramework\Db\Entity
261
	 * @throws StatusException
262
	 * @throws \OCA\Deck\NoPermissionException
263
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
264
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
265
	 * @throws BadRequestException
266
	 */
267
	public function rename($id, $title) {
268
269
		if (is_numeric($id) === false) {
270
			throw new BadRequestException('id must be a number');
271
		}
272
273
		if ($title === false || $title === null) {
274
			throw new BadRequestException('title must be provided');
275
		}
276
277
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
278
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
279
			throw new StatusException('Operation not allowed. This board is archived.');
280
		}
281
		$card = $this->cardMapper->find($id);
282
		if ($card->getArchived()) {
283
			throw new StatusException('Operation not allowed. This card is archived.');
284
		}
285
		$card->setTitle($title);
286
		return $this->cardMapper->update($card);
287
	}
288
289
	/**
290
	 * @param $id
291
	 * @param $stackId
292
	 * @param $order
293
	 * @return array
294
	 * @throws StatusException
295
	 * @throws \OCA\Deck\NoPermissionException
296
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
297
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
298
	 * @throws BadRequestException
299
	 */
300
	public function reorder($id, $stackId, $order) {
301
302
		if (is_numeric($id) === false) {
303
			throw new BadRequestException('card id must be a number');
304
		}
305
306
		if (is_numeric($stackId) === false) {
307
			throw new BadRequestException('stack id must be a number');
308
		}
309
310
		if (is_numeric($order) === false) {
311
			throw new BadRequestException('order must be a number');
312
		}
313
314
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
315
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
316
			throw new StatusException('Operation not allowed. This board is archived.');
317
		}
318
		$cards = $this->cardMapper->findAll($stackId);
319
		$result = [];
320
		$i = 0;
321
		foreach ($cards as $card) {
322
			if ($card->getArchived()) {
323
				throw new StatusException('Operation not allowed. This card is archived.');
324
			}
325
			if ($card->id === $id) {
326
				$card->setOrder($order);
327
				$card->setLastModified(time());
328
			}
329
330
			if ($i === $order) {
331
				$i++;
332
			}
333
334
			if ($card->id !== $id) {
335
				$card->setOrder($i++);
336
			}
337
			$this->cardMapper->update($card);
338
			$result[$card->getOrder()] = $card;
339
		}
340
341
		return $result;
342
	}
343
344
	/**
345
	 * @param $id
346
	 * @return \OCP\AppFramework\Db\Entity
347
	 * @throws StatusException
348
	 * @throws \OCA\Deck\NoPermissionException
349
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
350
	 * @throws \OCP\AppFramework\Db\
351
	 * @throws BadRequestException
352
	 */
353 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...
354
355
		if (is_numeric($id) === false) {
356
			throw new BadRequestException('id must be a number');
357
		}
358
359
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
360
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
361
			throw new StatusException('Operation not allowed. This board is archived.');
362
		}
363
		$card = $this->cardMapper->find($id);
364
		$card->setArchived(true);
365
		$newCard = $this->cardMapper->update($card);
366
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_ARCHIVE);
367
		return $newCard;
368
	}
369
370
	/**
371
	 * @param $id
372
	 * @return \OCP\AppFramework\Db\Entity
373
	 * @throws StatusException
374
	 * @throws \OCA\Deck\NoPermissionException
375
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
376
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
377
	 * @throws BadRequestException
378
	 */
379 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...
380
381
		if (is_numeric($id) === false) {
382
			throw new BadRequestException('id must be a number');
383
		}
384
385
		$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
386
		if ($this->boardService->isArchived($this->cardMapper, $id)) {
387
			throw new StatusException('Operation not allowed. This board is archived.');
388
		}
389
		$card = $this->cardMapper->find($id);
390
		$card->setArchived(false);
391
		$newCard = $this->cardMapper->update($card);
392
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_UNARCHIVE);
393
		return $newCard;
394
	}
395
396
	/**
397
	 * @param $cardId
398
	 * @param $labelId
399
	 * @throws StatusException
400
	 * @throws \OCA\Deck\NoPermissionException
401
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
402
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
403
	 * @throws BadRequestException
404
	 */
405 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...
406
407
		if (is_numeric($cardId) === false) {
408
			throw new BadRequestException('card id must be a number');
409
		}
410
411
		if (is_numeric($labelId) === false) {
412
			throw new BadRequestException('label id must be a number');
413
		}
414
415
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
416
		if ($this->boardService->isArchived($this->cardMapper, $cardId)) {
417
			throw new StatusException('Operation not allowed. This board is archived.');
418
		}
419
		$card = $this->cardMapper->find($cardId);
420
		if ($card->getArchived()) {
421
			throw new StatusException('Operation not allowed. This card is archived.');
422
		}
423
		$label = $this->labelMapper->find($labelId);
424
		$this->cardMapper->assignLabel($cardId, $labelId);
425
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]);
426
	}
427
428
	/**
429
	 * @param $cardId
430
	 * @param $labelId
431
	 * @throws StatusException
432
	 * @throws \OCA\Deck\NoPermissionException
433
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
434
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
435
	 * @throws BadRequestException
436
	 */
437 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...
438
439
		if (is_numeric($cardId) === false) {
440
			throw new BadRequestException('card id must be a number');
441
		}
442
443
		if (is_numeric($labelId) === false) {
444
			throw new BadRequestException('label id must be a number');
445
		}
446
447
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
448
		if ($this->boardService->isArchived($this->cardMapper, $cardId)) {
449
			throw new StatusException('Operation not allowed. This board is archived.');
450
		}
451
		$card = $this->cardMapper->find($cardId);
452
		if ($card->getArchived()) {
453
			throw new StatusException('Operation not allowed. This card is archived.');
454
		}
455
		$label = $this->labelMapper->find($labelId);
456
		$this->cardMapper->removeLabel($cardId, $labelId);
457
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]);
458
	}
459
460
	/**
461
	 * @param $cardId
462
	 * @param $userId
463
	 * @return bool|null|\OCP\AppFramework\Db\Entity
464
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
465
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
466
	 * @throws BadRequestException
467
	 */
468
	public function assignUser($cardId, $userId) {
469
470
		if (is_numeric($cardId) === false) {
471
			throw new BadRequestException('card id must be a number');
472
		}
473
474
		if ($userId === false || $userId === null) {
475
			throw new BadRequestException('user id must be provided');
476
		}
477
478
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
479
		$assignments = $this->assignedUsersMapper->find($cardId);
480
		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...
481
			if ($assignment->getParticipant() === $userId) {
482
				return false;
483
			}
484
		}
485
		$card = $this->cardMapper->find($cardId);
486
487
		if ($userId !== $this->currentUser) {
488
			/* Notifyuser about the card assignment */
489
			$this->notificationHelper->sendCardAssigned($card, $userId);
490
		}
491
492
		$assignment = new AssignedUsers();
493
		$assignment->setCardId($cardId);
494
		$assignment->setParticipant($userId);
495
		$assignment = $this->assignedUsersMapper->insert($assignment);
496
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_ASSIGN, ['assigneduser' => $userId]);
497
		return $assignment;
498
	}
499
500
	/**
501
	 * @param $cardId
502
	 * @param $userId
503
	 * @return \OCP\AppFramework\Db\Entity
504
	 * @throws NotFoundException
505
	 * @throws \OCP\AppFramework\Db\DoesNotExistException
506
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
507
	 * @throws BadRequestException
508
	 */
509
	public function unassignUser($cardId, $userId) {
510
		$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
511
512
		if (is_numeric($cardId) === false) {
513
			throw new BadRequestException('card id must be a number');
514
		}
515
516
		if ($userId === false || $userId === null) {
517
			throw new BadRequestException('user must be provided');
518
		}
519
520
		$assignments = $this->assignedUsersMapper->find($cardId);
521
		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...
522
			if ($assignment->getParticipant() === $userId) {
523
				$assignment = $this->assignedUsersMapper->delete($assignment);
524
				$card = $this->cardMapper->find($cardId);
525
				$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_UNASSIGN, ['assigneduser' => $userId]);
526
				return $assignment;
527
			}
528
		}
529
		throw new NotFoundException('No assignment for ' . $userId . 'found.');
530
	}
531
}
532