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

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