PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.20
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.20
2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 1.45 All 41 releases
fluent-boards / app / Services / BoardService.php

BoardService.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.20, at app/Services/BoardService.php

904 lines 30.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\App\Services;
4
5 use FluentBoards\App\Models\Activity;
6 use FluentBoards\App\Models\Stage;
7 use FluentBoards\App\Models\User;
8 use FluentBoards\App\Models\Board;
9 use FluentBoards\App\Models\Relation;
10 use FluentBoards\App\Models\Task;
11 use FluentBoards\App\Models\Meta;
12 use FluentBoards\App\Models\TaskMeta;
13 use FluentBoards\App\Services\Libs\FileSystem;
14 use FluentBoards\Framework\Support\Arr;
15
16 class BoardService
17 {
18 public function getBoardsByType($type)
19 {
20 return Board::where('type', $type)->whereNull('archived_at')->orderBy('created_at', 'ASC')->get();
21 }
22
23 public function deleteBoard($boardId)
24 {
25 $board = Board::findOrFail($boardId);
26
27 $options = null;
28 //if we need to do something before a board is deleted
29 do_action('fluent_boards/before_board_deleted', $board, $options);
30
31 //related task delete, task related relations delete
32 $allTaskIdsInBoard = $board->tasks->pluck('id');
33 $taskRelatedRelations = Relation::whereIn('object_id', $allTaskIdsInBoard);
34 $taskRelatedRelations->delete();
35 TaskMeta::whereIn('task_id', $allTaskIdsInBoard)->delete();
36
37 Task::whereIn('id', $allTaskIdsInBoard)->delete();
38
39 //removing all Board Settings
40 $board->boardUserEmailNotificationSettings()->detach();
41 $board->boardUserNotificationSettings()->detach();
42 //removing all Board users
43 $board->users()->detach();
44
45 //removing add board stages
46 $board->stages()->delete();
47
48 //removing add board labels
49 $board->labels()->delete();
50
51 //removing add board comment
52 $board->comments()->delete();
53
54 foreach ($board->notifications as $notification) {
55 $notification->users()->detach();
56 }
57 $board->notifications()->delete();
58
59 //delete board related meta
60 $this->deleteBoardMeta($boardId);
61
62 //delete from recently viewed
63 $this->deleteFromRecentlyViewed($boardId);
64
65 $board->delete();
66 if(!defined('FLUENT_BOARDS_PRO')){
67 FileSystem::deleteDir('board_'.$boardId);
68 }
69 // do_action('fluent_boards/board_deleted', $board);
70 }
71
72 public function fetchBoardMeta($boardId)
73 {
74 $boardMeta = Meta::where('object_id', $boardId)
75 ->where('object_type', 'board')
76 ->where('key', 'is_auth_require')
77 ->orderBy('id', 'desc')->first();
78
79 if ($boardMeta) {
80 $boardMeta->value = maybe_unserialize($boardMeta->value);
81 return $boardMeta;
82 } else {
83 $meta = new Meta();
84 $settingData = array(
85 'is_auth_require_idea_submit' => '',
86 'is_auth_require_voting_commenting' => '',
87 'is_auth_require_reaction' => '',
88 'is_allow_email_along_with_auth' => '',
89 'is_allow_unauthentication_reaction_along_with_auth' => ''
90 );
91 $meta->object_id = $boardId;
92 $meta->object_type = 'board';
93 $meta->key = 'is_auth_require';
94 $meta->value = \maybe_serialize($settingData);
95 $meta->save();
96 $meta->value = $settingData;
97 return $meta;
98 }
99 }
100
101 public function modifyAuthenticationPermission($data, $boardId)
102 {
103 $boardMeta = Meta::where('object_id', $boardId)
104 ->where('object_type', 'board')
105 ->where('key', 'is_auth_require')
106 ->orderBy('id', 'desc')->first();
107
108 if ($boardMeta) {
109 $settings = array(
110 'is_auth_require_idea_submit' => $data['is_auth_require_idea_submit'],
111 'is_auth_require_voting_commenting' => $data['is_auth_require_voting_commenting'],
112 'is_auth_require_reaction' => $data['is_auth_require_reaction'],
113 'is_allow_email_along_with_auth' => $data['is_allow_email_along_with_auth'],
114 'is_allow_unauthentication_reaction_along_with_auth' => $data['is_allow_unauthentication_reaction_along_with_auth']
115 );
116 $boardMeta->value = \maybe_serialize($settings);
117 $boardMeta->save();
118 }
119 return $boardMeta;
120 }
121
122 public function createBoard($boardData)
123 {
124 $boardData = [
125 'title' => $boardData['title'],
126 'type' => $boardData['type'] ? $boardData['type'] : 'todo',
127 'description' => $boardData['description'],
128 'currency' => isset($boardData['currency']) ? $boardData['currency'] : 'USD',
129 'background' => isset($boardData['background']) ? $boardData['background'] : '',
130 ];
131
132 $boardData = apply_filters('fluent_boards/before_create_board', $boardData);
133
134 $board = Board::create($boardData);
135
136 $this->setCurrentUserPreferencesOnBoardCreate($board);
137
138 return $board;
139 }
140
141 private function setCurrentUserPreferencesOnBoardCreate($board)
142 {
143 $board->users()->attach(
144 get_current_user_id(),
145 [
146 'object_type' => Constant::OBJECT_TYPE_BOARD_USER,
147 'settings' => maybe_serialize([
148 Constant::IS_BOARD_ADMIN => true
149 ]),
150 'preferences' => maybe_serialize(Constant::BOARD_NOTIFICATION_TYPES)
151 ]
152 );
153 }
154
155 public function removeUserFromBoard($boardId, $userId)
156 {
157
158 $board = Board::findOrFail($boardId);
159 $user = User::findOrFail($userId);
160
161 $board->users()->detach($userId);
162 $board->boardUserNotificationSettings()->detach($userId); //removing notification settings of user in that board
163 $board->boardUserEmailNotificationSettings()->detach($userId); //removing email notification settings of user in that board
164
165 //detacing all tasks of this board from user
166 $taskIdsToDetach = $user->tasks()->where('board_id', $boardId)->get()->pluck('id');
167
168 $user->tasks()->detach($taskIdsToDetach);
169 $user->watchingTasks()->detach($taskIdsToDetach);
170
171 // do_action('fluent_boards/board_member_removed', $boardId, $user->display_name);
172 }
173
174 public function removeFromRecentlyOpened($boardId, $userId)
175 {
176 $recentlyOpened = Meta::where('object_id', $userId)
177 ->where('object_type', Constant::OBJECT_TYPE_USER)
178 ->where('key', Constant::USER_RECENT_BOARDS)
179 ->first();
180 if ($recentlyOpened) {
181 $recentBoardIds = $recentlyOpened->value;
182
183 $index = array_search($boardId, $recentBoardIds);
184 array_splice($recentBoardIds, $index, 1);
185
186 $recentlyOpened->value = $recentBoardIds;
187 $recentlyOpened->save();
188 }
189
190 }
191
192 public function updateBoard($board, $data)
193 {
194 if ($data['title']) {
195 $data['title'] = $data['title'];
196 } else {
197 throw new \Exception('Title cannot be empty');
198 }
199 if (isset($data['description'])) {
200 $data['description'] = $data['description'];
201 }
202 $board->fill($data);
203 $board->save();
204 // do_action('fluent_boards/board_updated', $board);
205 return $board;
206 }
207
208 public function defaultStages()
209 {
210 $stages = [
211 (object)[
212 'group' => 'open',
213 'label' => 'Open',
214 ],
215 (object)[
216 'group' => 'in_progress',
217 'label' => 'In Progress',
218 ],
219 (object)[
220 'group' => 'completed',
221 'label' => 'Completed',
222 ],
223 ];
224
225 return serialize($this->processStages($stages));
226 }
227
228 public function changePositionOfStage($boardId, $changeData)
229 {
230 $allStages = Stage::where('board_id', $boardId)
231 ->where('is_archived', 0)
232 ->where('type', 'stage')
233 ->orderBy('position', 'asc')
234 ->get();
235
236 $changedStage = null;
237
238 foreach ($allStages as $stage) {
239 if ($changeData['fromPosition'] < $changeData['toPosition']) {
240 if ($stage['position'] == $changeData['fromPosition']) {
241 $stage['position'] = (int)$changeData['toPosition'];
242 $stage->save();
243 $changedStage = $stage;
244 } elseif (
245 $stage['position'] > $changeData['fromPosition'] &&
246 $stage['position'] <= $changeData['toPosition']
247 ) {
248 $stage['position'] = $stage->position - 1;
249 $stage->save();
250 }
251 } else {
252 if (
253 $stage['position'] >= $changeData['toPosition'] &&
254 $stage['position'] < $changeData['fromPosition']
255 ) {
256 $stage['position'] = $stage->position + 1;
257 $stage->save();
258 } elseif ($stage['position'] == $changeData['fromPosition']) {
259 $stage['position'] = (int)$changeData['toPosition'];
260 $stage->save();
261 $changedStage = $stage;
262 }
263 }
264 }
265
266 do_action('fluent_boards/board_stage_dragged', $changedStage);
267 }
268
269 public function rePositionStages($boardId, $incomingList)
270 {
271 $oldList = Stage::where('board_id', $boardId)->where('type', 'stage')->whereNull('archived_at')->orderBy('position')->pluck('id');
272
273 foreach ($incomingList as $key => $stage_id) {
274 $stage = Stage::findOrFail($stage_id);
275 $stage->moveToNewPosition($key + 1);
276 }
277 do_action('fluent_boards/board_stages_reordered', $boardId, $oldList);
278 }
279
280 public function processStages($stages)
281 {
282 $processedStages = [];
283 foreach ($stages as $stage) {
284 if (is_object($stage)) {
285 $processedStages[] = (object)[
286 'group' => Helper::snake_case($stage->slug),
287 'label' => sanitize_text_field($stage->label)
288 ];
289 } else {
290 $processedStages[] = (object)[
291 'group' => Helper::snake_case($stage['group']),
292 'label' => sanitize_text_field($stage['label'])
293 ];
294 }
295 }
296 return $processedStages;
297 }
298
299 public function archiveStage($id, $stage)
300 {
301 $stage->archived_at = current_time('mysql');
302 $stage->position = 0;
303 $stage->save();
304
305 do_action('fluent_boards/board_stage_archived', $id, $stage->title);
306 return $stage;
307 }
308
309 public function restoreStage($id, $stage)
310 {
311 $stageService = new StageService();
312 $lastStagePosition = $stageService->getLastPositionOfStagesOfBoard($stage->board_id);
313 $stage->archived_at = null;
314 $stage->position = $lastStagePosition ? $lastStagePosition->position + 1 : 1;
315 $stage->save();
316 do_action('fluent_boards/board_stage_restored', $id, $stage->title);
317 return $stage;
318 }
319
320 public function getActivities($id, $data)
321 {
322 $per_page = isset($data['per_page']) ? $data['per_page'] : 40;
323 $page = isset($data['page']) ? $data['page'] : 1;
324 return Activity::where('object_id', $id)->where('object_type', Constant::ACTIVITY_BOARD)->with(['user'])
325 ->orderBy('id', 'DESC')
326 ->paginate($per_page, ['*'], 'page', $page);
327 }
328
329 public function isAlreadyMember($boardId, $memberId)
330 {
331 $isAlreadyMember = Relation::where('object_id', $boardId)
332 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
333 ->where('foreign_id', $memberId)->first();
334
335 return $isAlreadyMember ?? false;
336 }
337
338 public function addMembersInBoard($boardId, $memberId)
339 {
340 $board = Board::find($boardId);
341
342 if (!$board) {
343 return false;
344 }
345
346 $board->users()->attach(
347 $memberId,
348 [
349 'object_type' => Constant::OBJECT_TYPE_BOARD_USER,
350 'settings' => maybe_serialize(Constant::BOARD_USER_SETTINGS),
351 'preferences' => maybe_serialize(Constant::BOARD_NOTIFICATION_TYPES)
352 ]
353 );
354 $boardMember = User::find($memberId);
355 do_action('fluent_boards/board_member_added', $boardId, $boardMember->display_name);
356 return $boardMember;
357 }
358
359 public function makeAdminOfBoard($boardId, $userId)
360 {
361 $boardUser = Relation::where('object_id', $boardId)
362 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
363 ->where('foreign_id', $userId)->first();
364 $boardUser->settings = [
365 'is_admin' => true
366 ];
367 $boardUser->save();
368
369 $user = User::findOrFail($userId);
370 do_action('fluent_boards/board_admin_added', $boardId, $userId);
371 $user['is_admin'] = true;
372 $user['is_board_admin'] = true;
373 return $user;
374 }
375
376 public function removeAdminFromBoard($boardId, $userId)
377 {
378 $boardUser = Relation::where('object_id', $boardId)
379 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
380 ->where('foreign_id', $userId)->first();
381
382 $boardUser->settings = [
383 'is_admin' => false
384 ];
385
386 $boardUser->save();
387 $user = User::findOrFail($userId);
388 do_action('fluent_boards/board_admin_removed', $boardId, $userId);
389 $user['is_admin'] = false;
390 $user['is_board_admin'] = false;
391 return $user;
392 }
393
394 public function getUsersOfBoards()
395 {
396 $userBoards = Relation::whereNotNull('board_id')
397 ->where('user_id', get_current_user_id())
398 ->where('status', 'ACTIVE')->get();
399
400 return $userBoards;
401 }
402
403 /**
404 * change board background
405 * @param mixed $backgroundData
406 * @return string
407 */
408
409 public function setBoardBackground($backgroundData, $board_id)
410 {
411 $board = Board::find($board_id);
412 $background = $board->background;
413
414 // if board background has color
415 if (isset($backgroundData['color'])) {
416 $background['color'] = $backgroundData['color'];
417 $background['image_url'] = null;
418 $background['is_image'] = false;
419 }
420
421 // if board background has image
422 if (isset($backgroundData['image_url'])) {
423 $background['image_url'] = $backgroundData['image_url'];
424 $background['is_image'] = true;
425 $background['color'] = null;
426 }
427 $background['id'] = $backgroundData['id'];
428
429 $board->background = $background;
430 $board->save();
431
432 return $board->background;
433 }
434
435
436 /**
437 * Summary of getStageTaskAvailablePositions
438 * @param mixed $board_id
439 * @param mixed $stage_slug
440 * @return array of available positions of the stage with one increased value because if the stage has 10 tasks than it will have 10 position and +1 as last position of the stage
441 */
442 public function getStageTaskAvailablePositions($board_id, $stage_id)
443 {
444 $availablePositions = Task::query()
445 ->where('board_id', $board_id)
446 ->where('parent_id', null)
447 ->where('stage_id', $stage_id)
448 ->whereNull('archived_at')
449 ->orderBy('position', 'asc')
450 ->get()
451 ->pluck('position')->toArray();
452
453 $totalPosition = count($availablePositions);
454 $availablePositions[$totalPosition] = $totalPosition + 1;
455
456 return $availablePositions;
457 }
458
459 public function getAssigneesByBoard($board_id, $search = '')
460 {
461 $assignees = [];
462 $boardUsers = [];
463 $board = Board::with('users')->find($board_id);
464
465 if ($board) {
466 if ($search) {
467 $boardUsers = $board->users->filter(
468 function ($user) use ($search) {
469 return strpos($user->display_name, $search) !== false || strpos($user->user_email, $search) !== false;
470 }
471 );
472 } else {
473 $boardUsers = $board->users;
474 }
475 };
476 foreach ($boardUsers as $user) {
477 $taskAssignee = Relation::where('foreign_id', $user->ID)->where('object_type', 'task_assignee')->exists();
478 if ($taskAssignee) {
479 $assignees[] = $user;
480 }
481 }
482 return $assignees;
483 }
484
485 private function deleteFromRecentlyViewed($boardId)
486 {
487 $recentlyOpened = $this->recentlyViewedByUserQuery()->first();
488 if ($recentlyOpened) {
489 $recentBoardIds = $recentlyOpened->value;
490 if (in_array($boardId, $recentBoardIds)) {
491 $index = array_search($boardId, $recentBoardIds);
492 unset($recentBoardIds[$index]);
493 $recentlyOpened->value = $recentBoardIds;
494 $recentlyOpened->save();
495 }
496 }
497 }
498
499 public function updateRecentBoards($boardId)
500 {
501 $userId = get_current_user_id();
502 $recentlyOpened = $this->recentlyViewedByUserQuery($userId)->first();
503 if (!$recentlyOpened) {
504 $openedBoards = [$boardId];
505 $userMeta = new Meta();
506 $userMeta->object_id = $userId;
507 $userMeta->object_type = Constant::OBJECT_TYPE_USER;
508 $userMeta->key = Constant::USER_RECENT_BOARDS;
509 $userMeta->value = $openedBoards;
510 $userMeta->save();
511 } else {
512 $recentBoardIds = $recentlyOpened->value;
513 // Ensure the value is an array
514 if (!is_array($recentBoardIds)) {
515 $recentBoardIds = [];
516 }
517
518 // Check if the board is already in the list
519 if (!in_array($boardId, $recentBoardIds)) {
520 // If there are already 3 boards, remove the last one
521 if (count($recentBoardIds) >= 3) {
522 array_pop($recentBoardIds);
523 }
524 } else {
525 // Remove the existing board id to move it to the front
526 $index = array_search($boardId, $recentBoardIds);
527 unset($recentBoardIds[$index]);
528 }
529 // Add the board to the beginning of the list
530 array_unshift($recentBoardIds, $boardId);
531
532 // Update the meta value and save it
533 $recentlyOpened->value = $recentBoardIds;
534 $recentlyOpened->save();
535 }
536 }
537
538 public function recentlyViewedByUserQuery($userId = null)
539 {
540 if (!$userId) {
541 $userId = get_current_user_id();
542 }
543
544 return Meta::query()->where('object_id', $userId)
545 ->where('object_type', Constant::OBJECT_TYPE_USER)
546 ->where('key', Constant::USER_RECENT_BOARDS);
547 }
548
549 public function getRecentBoards()
550 {
551 $userId = get_current_user_id();
552
553 $recentBoardIds = $this->recentlyViewedByUserQuery($userId)->value('value');
554
555 if (!$recentBoardIds) {
556 return [];
557 }
558
559 $currentUser = User::find($userId);
560
561 if (!PermissionManager::isAdmin($userId)){
562 $recentBoardIds = array_intersect($recentBoardIds, $currentUser->whichBoards->pluck('id')->toArray());
563 }
564
565 // This is for checking if that board is exists
566 // TODO: we will remove this code in future version
567 if (!$this->recentBoardBackwardCompatibilityCheck()) {
568 foreach ($recentBoardIds as $index => $boardId) {
569 $board = Board::find($boardId);
570 if (!$board) {
571 $this->deleteFromRecentlyViewed($boardId);
572 unset($recentBoardIds[$index]);
573 }
574 }
575
576 $this->updateRecentBoardCheckMeta();
577 }
578
579 return Board::whereIn('id', $recentBoardIds)->withCount('completedTasks')->with(['stages', 'users'])->get();
580 }
581
582 public function getRecentBoardCheckMeta($userId = null){
583 if (!$userId) {
584 $userId = get_current_user_id();
585 }
586
587 return Meta::where('object_id', $userId)
588 ->where('object_type', Constant::OBJECT_TYPE_USER)
589 ->where('key', Constant::FBS_RECENTLY_VIEWED_CHECK)
590 ->first();
591 }
592
593 private function recentBoardBackwardCompatibilityCheck() {
594 $userId = get_current_user_id();
595
596 $checkedMeta = $this->getRecentBoardCheckMeta($userId);
597
598 if (!$checkedMeta) {
599 $recentBoardCheck = new Meta();
600 $recentBoardCheck->object_id = $userId;
601 $recentBoardCheck->object_type = Constant::OBJECT_TYPE_USER;
602 $recentBoardCheck->key = Constant::FBS_RECENTLY_VIEWED_CHECK;
603 $recentBoardCheck->value = 'no';
604 $recentBoardCheck->save();
605
606 return false;
607 } else {
608 if ($checkedMeta->value == 'yes') {
609 return true;
610 } else {
611 return false;
612 }
613 }
614 }
615
616 private function updateRecentBoardCheckMeta()
617 {
618 $checkedMeta = $this->getRecentBoardCheckMeta();
619
620 if ($checkedMeta) {
621 $checkedMeta->value = 'yes';
622 $checkedMeta->save();
623 }
624 }
625
626 public function updateAssociateMember($contactId, $boardId)
627 {
628 $contactOfBoard = $this->getAssociateMember($boardId, true);
629
630 if ($contactOfBoard) {
631 $contactOfBoard->value = $contactId;
632 $contactOfBoard->save();
633 } else {
634 $contactOfBoard = new Meta();
635 $contactOfBoard->object_id = $boardId;
636 $contactOfBoard->object_type = Constant::OBJECT_TYPE_BOARD;
637 $contactOfBoard->key = Constant::BOARD_ASSOCIATED_CRM_CONTACT;
638 $contactOfBoard->value = $contactId;
639 $contactOfBoard->save();
640 }
641
642 $board = Board::findOrFail($boardId);
643 do_action('fluent_boards/contact_added_to_board', $board, $contactId);
644
645 }
646
647 public function getAssociateMember($boardId, $fromUpdateMethod = false)
648 {
649
650 $contactOfBoard = Meta::query()->where('object_id', $boardId)
651 ->where('object_type', Constant::OBJECT_TYPE_BOARD)
652 ->where('key', Constant::BOARD_ASSOCIATED_CRM_CONTACT)
653 ->first();
654
655 if ($fromUpdateMethod) {
656 return $contactOfBoard;
657 }
658
659 if (!$contactOfBoard) {
660 return null;
661 }
662
663 return Helper::crm_contact($contactOfBoard->value);
664
665 // return \FluentCrm\App\Models\Subscriber::find($contactOfBoard->value);
666 }
667
668 public function deleteAssociateMember($boardId, $contact_id)
669 {
670 $contactOfBoard = Meta::query()->where('object_id', $boardId)
671 ->where('object_type', Constant::OBJECT_TYPE_BOARD)
672 ->where('key', Constant::BOARD_ASSOCIATED_CRM_CONTACT)
673 ->where('value', $contact_id)
674 ->first();
675
676 $contactOfBoard->delete();
677 }
678
679 public function sendInvitationToBoard($boardId, $email)
680 {
681 $user = User::query()->where('user_email', $email)->first();
682
683 if ($user) {
684 return $user;
685 }
686
687 $current_user_id = get_current_user_id();
688
689 do_action('fluent_boards/send_invitation', $boardId, $email, $current_user_id);
690
691 return;
692
693 }
694
695 public function getInvitations($boardId)
696 {
697 return Meta::query()->where('object_id', $boardId)
698 ->where('object_type', Constant::OBJECT_TYPE_BOARD)
699 ->where('key', Constant::BOARD_INVITATION)
700 ->get();
701 }
702
703 public function deleteInvitation($invitationId)
704 {
705 Meta::findOrFail($invitationId)->delete();
706 }
707
708 public function hasDataChanged($boardId)
709 {
710 $stages = [];
711 $labels = [];
712 $tasks = [];
713 $labels = [];
714 $taskDeleted = false;
715 $stageDeleted = false;
716 $labelDeleted = false;
717 $oneMinuteAgoTimestamp = current_time('timestamp') - 60; // Get the current timestamp and subtract 60 seconds
718 $oneMinuteAgo = date_i18n('Y-m-d H:i:s', $oneMinuteAgoTimestamp); // Format the timestamp into the desired format in GMT
719
720 $board = Board::find($boardId);
721 if (!$board) {
722 throw new \Exception("Board doesn't exists");
723 }
724 // if stage in this board has been deleted
725 $stageDeleted = Activity::where('object_id', $boardId)->where('updated_at', '>=', $oneMinuteAgo)->where('action', 'deleted')->where('column', 'stage')->exists();
726
727 if ($stageDeleted) {
728 $stages = $board->stages;
729 } else {
730 $stages = (new StageService())->getLastOneMinuteUpdatedStages($boardId);
731 }
732
733 $labelDeleted = Activity::where('object_id', $boardId)->where('updated_at', '>=', $oneMinuteAgo)->where('action', 'deleted')->where('column', 'label')->exists();
734 if ($labelDeleted) {
735 $labels = $board->labels;
736 } else {
737 $labels = (new LabelService())->getLastOneMinuteUpdatedLabels($boardId);
738 }
739
740 // if task in this board has been deleted
741 $taskDeletedOrMovedFormBoard = Activity::where('object_id', $boardId)->where('updated_at', '>=', $oneMinuteAgo)->where('action', 'deleted')->orWhere('action', 'moved')->where('column', 'task')->exists();
742
743 if ($taskDeletedOrMovedFormBoard) {
744 $tasks = Task::query()
745 ->where([
746 'board_id' => $boardId,
747 'parent_id' => null,
748 'archived_at' => null
749 ])
750 ->with(['assignees', 'labels'])
751 ->orderBy('due_at', 'ASC')
752 ->get();
753 } else {
754 $tasks = (new TaskService())->getLastOneMinuteUpdatedTasks($boardId);
755 }
756
757 foreach ($tasks as $task) {
758 $task->isOverdue = $task->isOverdue();
759 $task->isUpcoming = $task->upcoming();
760 }
761
762 if ($board->updated_at >= $oneMinuteAgo) {
763 $board->background = \maybe_unserialize($board->background);
764 } else {
765 $board = [];
766 }
767
768 return [
769 'board' => $board,
770 'stages' => $stages,
771 'labels' => $labels,
772 'tasks' => $tasks,
773 'taskDeleted' => $taskDeletedOrMovedFormBoard,
774 'stageDeleted' => $stageDeleted,
775 ];
776 }
777
778 public function getAssociatedBoards($associatedId)
779 {
780
781 $boardIds = Meta::query()->where('value', $associatedId)
782 ->where('object_type', Constant::OBJECT_TYPE_BOARD)
783 ->where('key', Constant::BOARD_ASSOCIATED_CRM_CONTACT)
784 ->pluck('object_id');
785
786 return Board::query()->whereIn('id', $boardIds)->with('stages', 'users')->get();
787 }
788
789 private function deleteBoardMeta($boardId)
790 {
791 Meta::where('object_id', $boardId)
792 ->where('object_type', Constant::OBJECT_TYPE_BOARD)
793 ->delete();
794 }
795
796 public function copyBoard($boardData)
797 {
798 $boardData = apply_filters('fluent_boards/before_create_board', $boardData);
799
800 $board = Board::create($boardData);
801
802 $this->setCurrentUserPreferencesOnBoardCreate($board);
803
804 return $board;
805 }
806
807 public function getBoardReports($board_id)
808 {
809 $taskQuery = Task::where('board_id', $board_id)
810 ->whereNull('parent_id')
811 ->whereNull('archived_at');
812
813 return $this->getTaskReports($taskQuery);
814 }
815
816 public function getAllBoardReports(){
817 $userId = get_current_user_id();
818
819 $taskQuery = Task::whereNull('parent_id')
820 ->whereNull('archived_at');
821
822 if (!PermissionManager::isAdmin($userId))
823 {
824 $currentUser = User::find($userId);
825 $relatedBoardIds = $currentUser->whichBoards->where('type', 'to-do')->pluck('id');
826 $taskQuery->whereIn('board_id', $relatedBoardIds);
827 }
828
829 return $this->getTaskReports($taskQuery);
830 }
831
832 private function getTaskReports($taskQuery)
833 {
834 $totalTasksQuery = clone $taskQuery;
835 $completedTaskQuery = clone $taskQuery;
836 $openTaskQuery = clone $taskQuery;
837 $overDueTaskQuery = clone $taskQuery;
838
839 $completedTaskCount = $completedTaskQuery->where('status', 'closed')->count();
840 $openTaskCount = $openTaskQuery->where('status', 'open')->count();
841 $overDueTasks = $overDueTaskQuery->overdue(true)->count();
842 $totalTasks = $totalTasksQuery->count();
843
844 $taskQuery->where('status', 'open');
845
846 $highQuery = clone $taskQuery;
847 $mediumQuery = clone $taskQuery;
848 $lowQuery = clone $taskQuery;
849
850 $high = $highQuery->where('priority', 'high')->count();
851 $low = $mediumQuery->where('priority', 'low')->count();
852 $medium = $lowQuery->where('priority', 'medium')->count();
853
854 $reportData = [
855 'completion' => [
856 'completed' => $completedTaskCount,
857 'incomplete' => $openTaskCount,
858 'overdue' => $overDueTasks,
859 'total' => $totalTasks
860 ],
861 'priority' => [
862 'high' => $high,
863 'medium' => $medium,
864 'low' => $low
865 ]
866
867 ];
868 return $reportData;
869 }
870
871 public function getStageWiseBoardReports($board_id)
872 {
873 $stages = Stage::where('board_id', $board_id)
874 ->where('type', 'stage')
875 ->whereNull('archived_at')
876 ->get();
877
878 foreach ($stages as $stage) {
879 $completedTaskCount = Task::where('stage_id', $stage->id)
880 ->where('status', 'closed')
881 ->count();
882
883 $openTaskCount = Task::where('stage_id', $stage->id)
884 ->whereNull('due_at')
885 ->where('status', 'open')
886 ->count();
887
888 $overDue = Task::where('stage_id', $stage->id)
889 ->whereNotNull('due_at')
890 ->where('status', 'open')
891 ->overdue(true)
892 ->count();
893
894 $stage->report = [
895 'completed' => $completedTaskCount,
896 'incomplete' => $openTaskCount,
897 'overdue' => $overDue
898 ];
899 }
900
901 return $stages;
902 }
903 }
904