PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.35
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.35
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.35, at app/Services/BoardService.php

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