Passed
Push — master ( b036ce...533801 )
by Julius
02:43 queued 11s
created

lib/Service/BoardService.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Julius Härtl <[email protected]>
4
 *
5
 * @author Julius Härtl <[email protected]>
6
 * @author Maxence Lange <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 *  This program is free software: you can redistribute it and/or modify
11
 *  it under the terms of the GNU Affero General Public License as
12
 *  published by the Free Software Foundation, either version 3 of the
13
 *  License, or (at your option) any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Affero General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Affero General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
namespace OCA\Deck\Service;
26
27
use OCA\Deck\Activity\ActivityManager;
28
use OCA\Deck\Activity\ChangeSet;
29
use OCA\Deck\Collaboration\Resources\ResourceProvider;
30
use OCA\Deck\Db\Acl;
31
use OCA\Deck\Db\AclMapper;
32
use OCA\Deck\Db\AssignedUsersMapper;
33
use OCA\Deck\Db\ChangeHelper;
34
use OCA\Deck\Db\IPermissionMapper;
35
use OCA\Deck\Db\Label;
36
use OCA\Deck\Db\StackMapper;
37
use OCA\Deck\NoPermissionException;
38
use OCA\Deck\Notification\NotificationHelper;
39
use OCP\AppFramework\Db\DoesNotExistException;
40
use OCP\IGroupManager;
41
use OCP\IL10N;
42
use OCA\Deck\Db\Board;
43
use OCA\Deck\Db\BoardMapper;
44
use OCA\Deck\Db\LabelMapper;
45
use OCP\IUserManager;
46
use OCA\Deck\BadRequestException;
47
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
48
use Symfony\Component\EventDispatcher\GenericEvent;
49
50
51
class BoardService {
52
53
	private $boardMapper;
54
	private $stackMapper;
55
	private $labelMapper;
56
	private $aclMapper;
57
	private $l10n;
58
	private $permissionService;
59
	private $notificationHelper;
60
	private $assignedUsersMapper;
61
	private $userManager;
62
	private $groupManager;
63
	private $userId;
64
	private $activityManager;
65
	/** @var EventDispatcherInterface */
66
	private $eventDispatcher;
67
	private $changeHelper;
68
69 View Code Duplication
	public function __construct(
0 ignored issues
show
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...
70
		BoardMapper $boardMapper,
71
		StackMapper $stackMapper,
72
		IL10N $l10n,
73
		LabelMapper $labelMapper,
74
		AclMapper $aclMapper,
75
		PermissionService $permissionService,
76
		NotificationHelper $notificationHelper,
77
		AssignedUsersMapper $assignedUsersMapper,
78
		IUserManager $userManager,
79
		IGroupManager $groupManager,
80
		ActivityManager $activityManager,
81
		EventDispatcherInterface $eventDispatcher,
82
		ChangeHelper $changeHelper,
83
		$userId
84
	) {
85
		$this->boardMapper = $boardMapper;
86
		$this->stackMapper = $stackMapper;
87
		$this->labelMapper = $labelMapper;
88
		$this->aclMapper = $aclMapper;
89
		$this->l10n = $l10n;
90
		$this->permissionService = $permissionService;
91
		$this->notificationHelper = $notificationHelper;
92
		$this->assignedUsersMapper = $assignedUsersMapper;
93
		$this->userManager = $userManager;
94
		$this->groupManager = $groupManager;
95
		$this->activityManager = $activityManager;
96
		$this->eventDispatcher = $eventDispatcher;
97
		$this->changeHelper = $changeHelper;
98
		$this->userId = $userId;
99
	}
100
101
	/**
102
	 * @return array
103
	 */
104
	public function findAll($since = -1, $details = null) {
105
		$userInfo = $this->getBoardPrerequisites();
106
		$userBoards = $this->boardMapper->findAllByUser($userInfo['user'], null, null, $since);
107
		$groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups'],null, null,  $since);
108
		$circleBoards = $this->boardMapper->findAllByCircles($userInfo['user'], null, null,  $since);
109
		$complete = array_merge($userBoards, $groupBoards, $circleBoards);
110
		$result = [];
111
		/** @var Board $item */
112
		foreach ($complete as &$item) {
113
			if (!array_key_exists($item->getId(), $result)) {
114
				$this->boardMapper->mapOwner($item);
115
				if ($item->getAcl() !== null) {
116
					foreach ($item->getAcl() as &$acl) {
117
						$this->boardMapper->mapAcl($acl);
118
					}
119
				}
120
				if ($details !== null) {
121
					$this->enrichWithStacks($item);
122
					$this->enrichWithLabels($item);
123
					$this->enrichWithUsers($item);
124
				}
125
				$permissions = $this->permissionService->matchPermissions($item);
126
				$item->setPermissions([
127
					'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
128
					'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT],
129
					'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE],
130
					'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE]
131
				]);
132
				$result[$item->getId()] = $item;
133
			}
134
		}
135
		return array_values($result);
136
	}
137
138
	/**
139
	 * @param $boardId
140
	 * @return Board
141
	 * @throws DoesNotExistException
142
	 * @throws \OCA\Deck\NoPermissionException
143
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
144
	 * @throws BadRequestException
145
	 */
146
	public function find($boardId) {
147
148
		if ( is_numeric($boardId) === false ) {
149
			throw new BadRequestException('board id must be a number');
150
		}
151
152
		$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
153
		/** @var Board $board */
154
		$board = $this->boardMapper->find($boardId, true, true);
155
		$this->boardMapper->mapOwner($board);
156
		foreach ($board->getAcl() as &$acl) {
157
			if ($acl !== null) {
158
				$this->boardMapper->mapAcl($acl);
159
			}
160
		}
161
		$permissions = $this->permissionService->matchPermissions($board);
162
		$board->setPermissions([
163
			'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
164
			'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT],
165
			'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE],
166
			'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE]
167
		]);
168
		$this->enrichWithUsers($board);
169
		return $board;
170
	}
171
172
	/**
173
	 * @return array
174
	 */
175
	private function getBoardPrerequisites() {
176
		$groups = $this->groupManager->getUserGroupIds(
177
			$this->userManager->get($this->userId)
178
		);
179
		return [
180
			'user' => $this->userId,
181
			'groups' => $groups
182
		];
183
	}
184
185
	/**
186
	 * @param $mapper
187
	 * @param $id
188
	 * @return bool
189
	 * @throws DoesNotExistException
190
	 * @throws \OCA\Deck\NoPermissionException
191
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
192
	 * @throws BadRequestException
193
	 */
194
	public function isArchived($mapper, $id) {
195
196
		if (is_numeric($id) === false)  {
197
			throw new BadRequestException('id must be a number');
198
		}
199
200
		try {
201
			$boardId = $id;
202
			if ($mapper instanceof IPermissionMapper) {
203
				$boardId = $mapper->findBoardId($id);
204
			}
205
			if ($boardId === null) {
206
				return false;
207
			}
208
		} catch (DoesNotExistException $exception) {
209
			return false;
210
		}
211
		$board = $this->find($boardId);
212
		return $board->getArchived();
213
	}
214
215
	/**
216
	 * @param $mapper
217
	 * @param $id
218
	 * @return bool
219
	 * @throws DoesNotExistException
220
	 * @throws \OCA\Deck\NoPermissionException
221
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
222
	 * @throws BadRequestException
223
	 */
224
	public function isDeleted($mapper, $id) {
225
226
		if ($mapper === false || $mapper === null) {
227
			throw new BadRequestException('mapper must be provided');
228
		}
229
230
		if (is_numeric($id) === false)  {
231
			throw new BadRequestException('id must be a number');
232
		}
233
234
		try {
235
			$boardId = $id;
236
			if ($mapper instanceof IPermissionMapper) {
237
				$boardId = $mapper->findBoardId($id);
238
			}
239
			if ($boardId === null) {
240
				return false;
241
			}
242
		} catch (DoesNotExistException $exception) {
243
			return false;
244
		}
245
		$board = $this->find($boardId);
246
		return $board->getDeletedAt() > 0;
247
	}
248
249
250
	/**
251
	 * @param $title
252
	 * @param $userId
253
	 * @param $color
254
	 * @return \OCP\AppFramework\Db\Entity
255
	 * @throws BadRequestException
256
	 */
257
	public function create($title, $userId, $color) {
258
259
		if ($title === false || $title === null) {
260
			throw new BadRequestException('title must be provided');
261
		}
262
263
		if ($userId === false || $userId === null) {
264
			throw new BadRequestException('userId must be provided');
265
		}
266
267
		if ($color === false || $color === null) {
268
			throw new BadRequestException('color must be provided');
269
		}
270
271
		if (!$this->permissionService->canCreate()) {
272
			throw new NoPermissionException('Creating boards has been disabled for your account.');
273
		}
274
275
		$board = new Board();
276
		$board->setTitle($title);
277
		$board->setOwner($userId);
278
		$board->setColor($color);
279
		$new_board = $this->boardMapper->insert($board);
280
281
		// create new labels
282
		$default_labels = [
283
			'31CC7C' => $this->l10n->t('Finished'),
284
			'317CCC' => $this->l10n->t('To review'),
285
			'FF7A66' => $this->l10n->t('Action needed'),
286
			'F1DB50' => $this->l10n->t('Later')
287
		];
288
		$labels = [];
289
		foreach ($default_labels as $labelColor => $labelTitle) {
290
			$label = new Label();
291
			$label->setColor($labelColor);
292
			$label->setTitle($labelTitle);
293
			$label->setBoardId($new_board->getId());
294
			$labels[] = $this->labelMapper->insert($label);
295
		}
296
		$new_board->setLabels($labels);
297
		$this->boardMapper->mapOwner($new_board);
298
		$permissions = $this->permissionService->matchPermissions($new_board);
299
		$new_board->setPermissions([
300
			'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
301
			'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT],
302
			'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE],
303
			'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE]
304
		]);
305
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $new_board, ActivityManager::SUBJECT_BOARD_CREATE);
306
		$this->changeHelper->boardChanged($new_board->getId());
307
308
		$this->eventDispatcher->dispatch(
309
			'\OCA\Deck\Board::onCreate',
310
			new GenericEvent(
311
				null, ['id' => $new_board->getId(), 'userId' => $userId, 'board' => $new_board]
312
			)
313
		);
314
315
		return $new_board;
316
	}
317
318
	/**
319
	 * @param $id
320
	 * @return Board
321
	 * @throws DoesNotExistException
322
	 * @throws \OCA\Deck\NoPermissionException
323
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
324
	 * @throws BadRequestException
325
	 */
326
	public function delete($id) {
327
328
		if (is_numeric($id) === false) {
329
			throw new BadRequestException('board id must be a number');
330
		}
331
332
		$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
333
		$board = $this->find($id);
334
		if ($board->getDeletedAt() > 0) {
335
			throw new BadRequestException('This board has already been deleted');
336
		}
337
		$board->setDeletedAt(time());
338
		$board = $this->boardMapper->update($board);
339
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $board, ActivityManager::SUBJECT_BOARD_DELETE);
340
		$this->changeHelper->boardChanged($board->getId());
341
342
		$this->eventDispatcher->dispatch(
343
			'\OCA\Deck\Board::onDelete', new GenericEvent(null, ['id' => $id])
344
		);
345
346
		return $board;
347
	}
348
349
	/**
350
	 * @param $id
351
	 * @return \OCP\AppFramework\Db\Entity
352
	 * @throws DoesNotExistException
353
	 * @throws \OCA\Deck\NoPermissionException
354
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
355
	 */
356 View Code Duplication
	public function deleteUndo($id) {
357
358
		if (is_numeric($id) === false) {
359
			throw new BadRequestException('board id must be a number');
360
		}
361
362
		$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
363
		$board = $this->find($id);
364
		$board->setDeletedAt(0);
365
		$board = $this->boardMapper->update($board);
366
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $board, ActivityManager::SUBJECT_BOARD_RESTORE);
367
		$this->changeHelper->boardChanged($board->getId());
368
369
		$this->eventDispatcher->dispatch(
370
			'\OCA\Deck\Board::onUpdate', new GenericEvent(null, ['id' => $id, 'board' => $board])
371
		);
372
373
		return $board;
374
	}
375
376
	/**
377
	 * @param $id
378
	 * @return \OCP\AppFramework\Db\Entity
379
	 * @throws DoesNotExistException
380
	 * @throws \OCA\Deck\NoPermissionException
381
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
382
	 * @throws BadRequestException
383
	 */
384
	public function deleteForce($id) {
385
		if (is_numeric($id) === false)  {
386
			throw new BadRequestException('id must be a number');
387
		}
388
389
		$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
390
		$board = $this->find($id);
391
		$delete = $this->boardMapper->delete($board);
392
393
		$this->eventDispatcher->dispatch(
394
			'\OCA\Deck\Board::onDelete', new GenericEvent(null, ['id' => $id])
395
		);
396
397
		return $delete;
398
	}
399
400
	/**
401
	 * @param $id
402
	 * @param $title
403
	 * @param $color
404
	 * @param $archived
405
	 * @return \OCP\AppFramework\Db\Entity
406
	 * @throws DoesNotExistException
407
	 * @throws \OCA\Deck\NoPermissionException
408
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
409
	 * @throws BadRequestException
410
	 */
411
	public function update($id, $title, $color, $archived) {
412
413
		if (is_numeric($id) === false) {
414
			throw new BadRequestException('board id must be a number');
415
		}
416
417
		if ($title === false || $title === null) {
418
			throw new BadRequestException('color must be provided');
419
		}
420
421
		if ($color === false || $color === null) {
422
			throw new BadRequestException('color must be provided');
423
		}
424
425
		if ( is_bool($archived) === false ) {
426
			throw new BadRequestException('archived must be a boolean');
427
		}
428
429
		$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
430
		$board = $this->find($id);
431
		$changes = new ChangeSet($board);
432
		$board->setTitle($title);
433
		$board->setColor($color);
434
		$board->setArchived($archived);
435
		$changes->setAfter($board);
436
		$this->boardMapper->update($board); // operate on clone so we can check for updated fields
437
		$this->boardMapper->mapOwner($board);
438
		$this->activityManager->triggerUpdateEvents(ActivityManager::DECK_OBJECT_BOARD, $changes, ActivityManager::SUBJECT_BOARD_UPDATE);
439
		$this->changeHelper->boardChanged($board->getId());
440
441
		$this->eventDispatcher->dispatch(
442
			'\OCA\Deck\Board::onUpdate', new GenericEvent(null, ['id' => $id, 'board' => $board])
443
		);
444
445
		return $board;
446
	}
447
448
449
	/**
450
	 * @param $boardId
451
	 * @param $type
452
	 * @param $participant
453
	 * @param $edit
454
	 * @param $share
455
	 * @param $manage
456
	 * @return \OCP\AppFramework\Db\Entity
457
	 * @throws BadRequestException
458
	 * @throws \OCA\Deck\NoPermissionException
459
	 */
460
	public function addAcl($boardId, $type, $participant, $edit, $share, $manage) {
461
462
		if (is_numeric($boardId) === false) {
463
			throw new BadRequestException('board id must be a number');
464
		}
465
466
		if ($type === false || $type === null) {
467
			throw new BadRequestException('type must be provided');
468
		}
469
470
		if ($participant === false || $participant === null) {
471
			throw new BadRequestException('participant must be provided');
472
		}
473
474
		if ($edit === null) {
475
			throw new BadRequestException('edit must be provided');
476
		}
477
478
		if ($share === null) {
479
			throw new BadRequestException('share must be provided');
480
		}
481
482
		if ($manage === null) {
483
			throw new BadRequestException('manage must be provided');
484
		}
485
486
		$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_SHARE);
487
		$acl = new Acl();
488
		$acl->setBoardId($boardId);
489
		$acl->setType($type);
490
		$acl->setParticipant($participant);
491
		$acl->setPermissionEdit($edit);
492
		$acl->setPermissionShare($share);
493
		$acl->setPermissionManage($manage);
494
495
		/* Notify users about the shared board */
496
		$this->notificationHelper->sendBoardShared($boardId, $acl);
497
498
		$newAcl = $this->aclMapper->insert($acl);
499
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $newAcl, ActivityManager::SUBJECT_BOARD_SHARE);
500
		$this->boardMapper->mapAcl($newAcl);
501
		$this->changeHelper->boardChanged($boardId);
502
503
		// TODO: use the dispatched event for this
504
		$version = \OC_Util::getVersion()[0];
505 View Code Duplication
		if ($version >= 16) {
506
			try {
507
				$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
508
				$resourceProvider->invalidateAccessCache($boardId);
509
			} catch (\Exception $e) {}
510
		}
511
512
		$this->eventDispatcher->dispatch(
513
			'\OCA\Deck\Board::onShareNew', new GenericEvent(null, ['id' => $newAcl->getId(), 'acl' => $newAcl, 'boardId' => $boardId])
514
		);
515
516
		return $newAcl;
517
	}
518
519
	/**
520
	 * @param $id
521
	 * @param $edit
522
	 * @param $share
523
	 * @param $manage
524
	 * @return \OCP\AppFramework\Db\Entity
525
	 * @throws DoesNotExistException
526
	 * @throws \OCA\Deck\NoPermissionException
527
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
528
	 * @throws BadRequestException
529
	 */
530
	public function updateAcl($id, $edit, $share, $manage) {
531
532
		if (is_numeric($id) === false) {
533
			throw new BadRequestException('id must be a number');
534
		}
535
536
		if ($edit === null) {
537
			throw new BadRequestException('edit must be provided');
538
		}
539
540
		if ($share === null) {
541
			throw new BadRequestException('share must be provided');
542
		}
543
544
		if ($manage === null) {
545
			throw new BadRequestException('manage must be provided');
546
		}
547
548
		$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
549
		/** @var Acl $acl */
550
		$acl = $this->aclMapper->find($id);
551
		$acl->setPermissionEdit($edit);
552
		$acl->setPermissionShare($share);
553
		$acl->setPermissionManage($manage);
554
		$this->boardMapper->mapAcl($acl);
555
		$board = $this->aclMapper->update($acl);
556
		$this->changeHelper->boardChanged($acl->getBoardId());
557
558
		$this->eventDispatcher->dispatch(
559
			'\OCA\Deck\Board::onShareEdit', new GenericEvent(null, ['id' => $id, 'boardId' => $acl->getBoardId(), 'acl' => $acl])
560
		);
561
562
		return $board;
563
	}
564
565
	/**
566
	 * @param $id
567
	 * @return \OCP\AppFramework\Db\Entity
568
	 * @throws DoesNotExistException
569
	 * @throws \OCA\Deck\NoPermissionException
570
	 * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
571
	 * @throws BadRequestException
572
	 */
573
	public function deleteAcl($id) {
574
575
		if (is_numeric($id) === false) {
576
			throw new BadRequestException('id must be a number');
577
		}
578
579
		$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
580
		/** @var Acl $acl */
581
		$acl = $this->aclMapper->find($id);
582
		$this->boardMapper->mapAcl($acl);
583
		if ($acl->getType() === Acl::PERMISSION_TYPE_USER) {
584
			$assignements = $this->assignedUsersMapper->findByUserId($acl->getParticipant());
585
			foreach ($assignements as $assignement) {
586
				$this->assignedUsersMapper->delete($assignement);
587
			}
588
		}
589
		$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $acl, ActivityManager::SUBJECT_BOARD_UNSHARE);
590
		$this->changeHelper->boardChanged($acl->getBoardId());
591
592
		$version = \OC_Util::getVersion()[0];
593 View Code Duplication
		if ($version >= 16) {
594
			try {
595
				$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
596
				$resourceProvider->invalidateAccessCache($acl->getBoardId());
597
			} catch (\Exception $e) {}
598
		}
599
		$delete = $this->aclMapper->delete($acl);
600
601
		$this->eventDispatcher->dispatch(
602
			'\OCA\Deck\Board::onShareDelete', new GenericEvent(null, ['id' => $id, 'boardId' => $acl->getBoardId(), 'acl' => $acl])
603
		);
604
605
		return $delete;
606
	}
607
608
	private function enrichWithStacks($board, $since = -1) {
609
		$stacks = $this->stackMapper->findAll($board->getId(), null, null, $since);
610
611
		if(\count($stacks) === 0) {
612
			return;
613
		}
614
615
		$board->setStacks($stacks);
616
	}
617
618
	private function enrichWithLabels($board, $since = -1) {
619
		$labels = $this->labelMapper->findAll($board->getId(), null, null, $since);
620
621
		if(\count($labels) === 0) {
622
			return;
623
		}
624
625
		$board->setLabels($labels);
626
	}
627
628
	private function enrichWithUsers($board, $since = -1) {
629
		$boardUsers = $this->permissionService->findUsers($board->getId());
630
		if(\count($boardUsers) === 0) {
631
			return;
632
		}
633
		$board->setUsers(array_values($boardUsers));
634
	}
635
636
}
637