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

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