whereNull('archived_at')->orderBy('created_at', 'ASC')->get(); } public function deleteBoard($boardId) { $board = Board::findOrFail($boardId); $options = null; //if we need to do something before a board is deleted do_action('fluent_boards/before_board_deleted', $board, $options); //related task delete, task related relations delete $allTaskIdsInBoard = $board->tasks->pluck('id'); $taskRelatedRelations = Relation::whereIn('object_id', $allTaskIdsInBoard); $taskRelatedRelations->delete(); TaskMeta::whereIn('task_id', $allTaskIdsInBoard)->delete(); Task::whereIn('id', $allTaskIdsInBoard)->delete(); //removing all Board Settings $board->boardUserEmailNotificationSettings()->detach(); $board->boardUserNotificationSettings()->detach(); //removing all Board users $board->users()->detach(); //removing add board stages $board->stages()->delete(); //removing add board labels $board->labels()->delete(); //removing add board comment $board->comments()->delete(); foreach ($board->notifications as $notification) { $notification->users()->detach(); } $board->notifications()->delete(); //delete board related meta $this->deleteBoardMeta($boardId); $board->delete(); // do_action('fluent_boards/board_deleted', $board); } public function fetchBoardMeta($boardId) { $boardMeta = Meta::where('object_id', $boardId) ->where('object_type', 'board') ->where('key', 'is_auth_require') ->orderBy('id', 'desc')->first(); if ($boardMeta) { $boardMeta->value = maybe_unserialize($boardMeta->value); return $boardMeta; } else { $meta = new Meta(); $settingData = array( 'is_auth_require_idea_submit' => '', 'is_auth_require_voting_commenting' => '', 'is_auth_require_reaction' => '', 'is_allow_email_along_with_auth' => '', 'is_allow_unauthentication_reaction_along_with_auth' => '' ); $meta->object_id = $boardId; $meta->object_type = 'board'; $meta->key = 'is_auth_require'; $meta->value = \maybe_serialize($settingData); $meta->save(); $meta->value = $settingData; return $meta; } } public function modifyAuthenticationPermission($data, $boardId) { $boardMeta = Meta::where('object_id', $boardId) ->where('object_type', 'board') ->where('key', 'is_auth_require') ->orderBy('id', 'desc')->first(); if ($boardMeta) { $settings = array( 'is_auth_require_idea_submit' => $data['is_auth_require_idea_submit'], 'is_auth_require_voting_commenting' => $data['is_auth_require_voting_commenting'], 'is_auth_require_reaction' => $data['is_auth_require_reaction'], 'is_allow_email_along_with_auth' => $data['is_allow_email_along_with_auth'], 'is_allow_unauthentication_reaction_along_with_auth' => $data['is_allow_unauthentication_reaction_along_with_auth'] ); $boardMeta->value = \maybe_serialize($settings); $boardMeta->save(); } return $boardMeta; } public function createBoard($boardData) { $boardData = [ 'title' => $boardData['title'], 'type' => $boardData['type'] ? $boardData['type'] : 'todo', 'description' => $boardData['description'], 'currency' => isset($boardData['currency']) ? $boardData['currency'] : 'USD', 'background' => isset($boardData['background']) ? $boardData['background'] : '', ]; $boardData = apply_filters('fluent_boards/before_create_board', $boardData); $board = Board::create($boardData); $this->setCurrentUserPreferencesOnBoardCreate($board); return $board; } private function setCurrentUserPreferencesOnBoardCreate($board) { $board->users()->attach( get_current_user_id(), [ 'object_type' => Constant::OBJECT_TYPE_BOARD_USER, 'settings' => maybe_serialize([ Constant::IS_BOARD_ADMIN => true ]), 'preferences' => maybe_serialize(Constant::BOARD_NOTIFICATION_TYPES) ] ); } public function removeUserFromBoard($boardId, $userId) { $board = Board::findOrFail($boardId); $user = User::findOrFail($userId); $board->users()->detach($userId); $board->boardUserNotificationSettings()->detach($userId); //removing notification settings of user in that board $board->boardUserEmailNotificationSettings()->detach($userId); //removing email notification settings of user in that board //detacing all tasks of this board from user $taskIdsToDetach = $user->tasks()->where('board_id', $boardId)->get()->pluck('id'); $user->tasks()->detach($taskIdsToDetach); $user->watchingTasks()->detach($taskIdsToDetach); // do_action('fluent_boards/board_member_removed', $boardId, $user->display_name); } public function removeFromRecentlyOpened($boardId, $userId) { $recentlyOpened = Meta::where('object_id', $userId) ->where('object_type', Constant::OBJECT_TYPE_USER) ->where('key', Constant::USER_RECENT_BOARDS) ->first(); if ($recentlyOpened) { $recentBoardIds = $recentlyOpened->value; $index = array_search($boardId, $recentBoardIds); array_splice($recentBoardIds, $index, 1); $recentlyOpened->value = $recentBoardIds; $recentlyOpened->save(); } } public function updateBoard($board, $data) { if ($data['title']) { $data['title'] = $data['title']; } else { throw new \Exception('Title cannot be empty'); } if (isset($data['description'])) { $data['description'] = $data['description']; } $board->fill($data); $board->save(); // do_action('fluent_boards/board_updated', $board); return $board; } public function defaultStages() { $stages = [ (object)[ 'group' => 'open', 'label' => 'Open', ], (object)[ 'group' => 'in_progress', 'label' => 'In Progress', ], (object)[ 'group' => 'completed', 'label' => 'Completed', ], ]; return serialize($this->processStages($stages)); } public function changePositionOfStage($boardId, $changeData) { $allStages = Stage::where('board_id', $boardId) ->where('is_archived', 0) ->where('type', 'stage') ->orderBy('position', 'asc') ->get(); $changedStage = null; foreach ($allStages as $stage) { if ($changeData['fromPosition'] < $changeData['toPosition']) { if ($stage['position'] == $changeData['fromPosition']) { $stage['position'] = (int)$changeData['toPosition']; $stage->save(); $changedStage = $stage; } elseif ( $stage['position'] > $changeData['fromPosition'] && $stage['position'] <= $changeData['toPosition'] ) { $stage['position'] = $stage->position - 1; $stage->save(); } } else { if ( $stage['position'] >= $changeData['toPosition'] && $stage['position'] < $changeData['fromPosition'] ) { $stage['position'] = $stage->position + 1; $stage->save(); } elseif ($stage['position'] == $changeData['fromPosition']) { $stage['position'] = (int)$changeData['toPosition']; $stage->save(); $changedStage = $stage; } } } do_action('fluent_boards/board_stage_dragged', $changedStage); } public function rePositionStages($boardId, $incomingList) { $oldList = Stage::where('board_id', $boardId)->where('type', 'stage')->whereNull('archived_at')->orderBy('position')->pluck('id'); foreach ($incomingList as $key => $stage_id) { $stage = Stage::findOrFail($stage_id); $stage->moveToNewPosition($key + 1); } do_action('fluent_boards/board_stages_reordered', $boardId, $oldList); } public function processStages($stages) { $processedStages = []; foreach ($stages as $stage) { if (is_object($stage)) { $processedStages[] = (object)[ 'group' => Helper::snake_case($stage->slug), 'label' => sanitize_text_field($stage->label) ]; } else { $processedStages[] = (object)[ 'group' => Helper::snake_case($stage['group']), 'label' => sanitize_text_field($stage['label']) ]; } } return $processedStages; } public function archiveStage($id, $stage) { $stage->archived_at = current_time('mysql'); $stage->position = 0; $stage->save(); do_action('fluent_boards/board_stage_archived', $id, $stage->title); return $stage; } public function restoreStage($id, $stage) { $stageService = new StageService(); $lastStagePosition = $stageService->getLastPositionOfStagesOfBoard($stage->board_id); $stage->archived_at = null; $stage->position = $lastStagePosition ? $lastStagePosition->position + 1 : 1; $stage->save(); do_action('fluent_boards/board_stage_restored', $id, $stage->title); return $stage; } public function getActivities($id, $data) { $per_page = isset($data['per_page']) ? $data['per_page'] : 40; $page = isset($data['page']) ? $data['page'] : 1; return Activity::where('object_id', $id)->where('object_type', Constant::ACTIVITY_BOARD)->with(['user']) ->orderBy('id', 'DESC') ->paginate($per_page, ['*'], 'page', $page); } public function isAlreadyMember($boardId, $memberId) { $isAlreadyMember = Relation::where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER) ->where('foreign_id', $memberId)->first(); return $isAlreadyMember ?? false; } public function addMembersInBoard($boardId, $memberId) { $board = Board::find($boardId); if (!$board) { return false; } $board->users()->attach( $memberId, [ 'object_type' => Constant::OBJECT_TYPE_BOARD_USER, 'settings' => maybe_serialize(Constant::BOARD_USER_SETTINGS), 'preferences' => maybe_serialize(Constant::BOARD_NOTIFICATION_TYPES) ] ); $boardMember = User::find($memberId); do_action('fluent_boards/board_member_added', $boardId, $boardMember->display_name); return $boardMember; } public function makeAdminOfBoard($boardId, $userId) { $boardUser = Relation::where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER) ->where('foreign_id', $userId)->first(); $boardUser->settings = [ 'is_admin' => true ]; $boardUser->save(); $user = User::findOrFail($userId); do_action('fluent_boards/board_admin_added', $boardId, $userId); $user['is_admin'] = true; $user['is_board_admin'] = true; return $user; } public function removeAdminFromBoard($boardId, $userId) { $boardUser = Relation::where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER) ->where('foreign_id', $userId)->first(); $boardUser->settings = [ 'is_admin' => false ]; $boardUser->save(); $user = User::findOrFail($userId); do_action('fluent_boards/board_admin_removed', $boardId, $userId); $user['is_admin'] = false; $user['is_board_admin'] = false; return $user; } public function getUsersOfBoards() { $userBoards = Relation::whereNotNull('board_id') ->where('user_id', get_current_user_id()) ->where('status', 'ACTIVE')->get(); return $userBoards; } /** * change board background * @param mixed $backgroundData * @return string */ public function setBoardBackground($backgroundData, $board_id) { $board = Board::find($board_id); $background = $board->background; // if board background has color if (isset($backgroundData['color'])) { $background['color'] = $backgroundData['color']; $background['image_url'] = null; $background['is_image'] = false; } // if board background has image if (isset($backgroundData['image_url'])) { $background['image_url'] = $backgroundData['image_url']; $background['is_image'] = true; $background['color'] = null; } $background['id'] = $backgroundData['id']; $board->background = $background; $board->save(); return $board->background; } /** * Summary of getStageTaskAvailablePositions * @param mixed $board_id * @param mixed $stage_slug * @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 */ public function getStageTaskAvailablePositions($board_id, $stage_id) { $availablePositions = Task::query() ->where('board_id', $board_id) ->where('parent_id', null) ->where('stage_id', $stage_id) ->whereNull('archived_at') ->orderBy('position', 'asc') ->get() ->pluck('position')->toArray(); $totalPosition = count($availablePositions); $availablePositions[$totalPosition] = $totalPosition + 1; return $availablePositions; } public function getAssigneesByBoard($board_id, $search = '') { $assignees = []; $boardUsers = []; $board = Board::with('users')->find($board_id); if ($board) { if ($search) { $boardUsers = $board->users->filter( function ($user) use ($search) { return strpos($user->display_name, $search) !== false || strpos($user->user_email, $search) !== false; } ); } else { $boardUsers = $board->users; } }; foreach ($boardUsers as $user) { $taskAssignee = Relation::where('foreign_id', $user->ID)->where('object_type', 'task_assignee')->exists(); if ($taskAssignee) { $assignees[] = $user; } } return $assignees; } public function updateRecentOpenedBoards($boardId) { $userId = get_current_user_id(); $recentlyOpened = Meta::where('object_id', $userId) ->where('object_type', Constant::OBJECT_TYPE_USER) ->where('key', Constant::USER_RECENT_BOARDS) ->first(); if (!$recentlyOpened) { $openedBoards = [$boardId]; $userMeta = new Meta(); $userMeta->object_id = $userId; $userMeta->object_type = Constant::OBJECT_TYPE_USER; $userMeta->key = Constant::USER_RECENT_BOARDS; $userMeta->value = $openedBoards; $userMeta->save(); } else { $boards = $recentlyOpened->value; if (in_array($boardId, $boards)) { $index = array_search($boardId, $boards); if ($index == 1) { $boards[1] = $boards[0]; $boards[0] = $boardId; } elseif ($index == 2) { $boards[2] = $boards[1]; $boards[1] = $boards[0]; $boards[0] = $boardId; } $recentlyOpened->value = $boards; $recentlyOpened->save(); } else { $boards[2] = $boards[1]; $boards[1] = $boards[0]; $boards[0] = $boardId; $recentlyOpened->value = $boards; $recentlyOpened->save(); } } } public function getRecentBoards() { $userId = get_current_user_id(); $recentBoardIds = Meta::query()->where('object_id', $userId) ->where('object_type', Constant::OBJECT_TYPE_USER) ->where('key', Constant::USER_RECENT_BOARDS) ->value('value'); if (!$recentBoardIds) { return []; } $currentUser = User::find($userId); if (!PermissionManager::isAdmin($userId)){ $recentBoardIds = array_intersect($recentBoardIds, $currentUser->whichBoards->pluck('id')->toArray()); } return Board::whereIn('id', $recentBoardIds)->withCount('completedTasks')->with(['stages', 'users'])->get(); } public function updateAssociateMember($contactId, $boardId) { $contactOfBoard = $this->getAssociateMember($boardId, true); if ($contactOfBoard) { $contactOfBoard->value = $contactId; $contactOfBoard->save(); } else { $contactOfBoard = new Meta(); $contactOfBoard->object_id = $boardId; $contactOfBoard->object_type = Constant::OBJECT_TYPE_BOARD; $contactOfBoard->key = Constant::BOARD_ASSOCIATED_CRM_CONTACT; $contactOfBoard->value = $contactId; $contactOfBoard->save(); } $board = Board::findOrFail($boardId); do_action('fluent_boards/contact_added_to_board', $board, $contactId); } public function getAssociateMember($boardId, $fromUpdateMethod = false) { $contactOfBoard = Meta::query()->where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD) ->where('key', Constant::BOARD_ASSOCIATED_CRM_CONTACT) ->first(); if ($fromUpdateMethod) { return $contactOfBoard; } if (!$contactOfBoard) { return null; } return Helper::crm_contact($contactOfBoard->value); // return \FluentCrm\App\Models\Subscriber::find($contactOfBoard->value); } public function deleteAssociateMember($boardId, $contact_id) { $contactOfBoard = Meta::query()->where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD) ->where('key', Constant::BOARD_ASSOCIATED_CRM_CONTACT) ->where('value', $contact_id) ->first(); $contactOfBoard->delete(); } public function sendInvitationToBoard($boardId, $email) { $user = User::query()->where('user_email', $email)->first(); if ($user) { return $user; } do_action('fluent_boards_pro/send_invitation', $boardId, $email); return; } public function getInvitations($boardId) { return Meta::query()->where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD) ->where('key', Constant::BOARD_INVITATION) ->get(); } public function deleteInvitation($invitationId) { Meta::findOrFail($invitationId)->delete(); } public function hasDataChanged($boardId) { $stages = []; $labels = []; $tasks = []; $labels = []; $taskDeleted = false; $stageDeleted = false; $labelDeleted = false; $oneMinuteAgoTimestamp = current_time('timestamp') - 60; // Get the current timestamp and subtract 60 seconds $oneMinuteAgo = date_i18n('Y-m-d H:i:s', $oneMinuteAgoTimestamp); // Format the timestamp into the desired format in GMT $board = Board::find($boardId); if (!$board) { throw new \Exception("Board doesn't exists"); } // if stage in this board has been deleted $stageDeleted = Activity::where('object_id', $boardId)->where('updated_at', '>=', $oneMinuteAgo)->where('action', 'deleted')->where('column', 'stage')->exists(); if ($stageDeleted) { $stages = $board->stages; } else { $stages = (new StageService())->getLastOneMinuteUpdatedStages($boardId); } $labelDeleted = Activity::where('object_id', $boardId)->where('updated_at', '>=', $oneMinuteAgo)->where('action', 'deleted')->where('column', 'label')->exists(); if ($labelDeleted) { $labels = $board->labels; } else { $labels = (new LabelService())->getLastOneMinuteUpdatedLabels($boardId); } // if task in this board has been deleted $taskDeletedOrMovedFormBoard = Activity::where('object_id', $boardId)->where('updated_at', '>=', $oneMinuteAgo)->where('action', 'deleted')->orWhere('action', 'moved')->where('column', 'task')->exists(); if ($taskDeletedOrMovedFormBoard) { $tasks = Task::query() ->where([ 'board_id' => $boardId, 'parent_id' => null, 'archived_at' => null ]) ->with(['assignees', 'labels']) ->orderBy('due_at', 'ASC') ->get(); } else { $tasks = (new TaskService())->getLastOneMinuteUpdatedTasks($boardId); } foreach ($tasks as $task) { $task->isOverdue = $task->isOverdue(); $task->isUpcoming = $task->upcoming(); } if ($board->updated_at >= $oneMinuteAgo) { $board->background = \maybe_unserialize($board->background); } else { $board = []; } return [ 'board' => $board, 'stages' => $stages, 'labels' => $labels, 'tasks' => $tasks, 'taskDeleted' => $taskDeletedOrMovedFormBoard, 'stageDeleted' => $stageDeleted, ]; } public function getAssociatedBoards($associatedId) { $boardIds = Meta::query()->where('value', $associatedId) ->where('object_type', Constant::OBJECT_TYPE_BOARD) ->where('key', Constant::BOARD_ASSOCIATED_CRM_CONTACT) ->pluck('object_id'); return Board::query()->whereIn('id', $boardIds)->with('stages', 'users')->get(); } private function deleteBoardMeta($boardId) { Meta::where('object_id', $boardId) ->where('object_type', Constant::OBJECT_TYPE_BOARD) ->delete(); } public function copyBoard($boardData) { $boardData = apply_filters('fluent_boards/before_create_board', $boardData); $board = Board::create($boardData); $this->setCurrentUserPreferencesOnBoardCreate($board); return $board; } public function getBoardReports($board_id) { $completedTaskCount = Task::where('board_id', $board_id) ->where('status', 'closed') ->whereNull('parent_id') ->whereNull('archived_at') ->count(); $openTaskCount = Task::where('board_id', $board_id) // ->whereNull('due_at') ->whereNull('parent_id') ->whereNull('archived_at') ->where('status', 'open') ->count(); $overDue = Task::where('board_id', $board_id) // ->whereNotNull('due_at') // ->where('status', 'open') ->whereNull('parent_id') ->whereNull('archived_at') ->overdue(true) ->count(); $totalTasks = Task::where('board_id', $board_id) ->whereNull('parent_id') ->whereNull('archived_at') ->count(); $high = Task::where('board_id', $board_id)->where('status', 'open')->where('priority', 'high')->count(); $medium = Task::where('board_id', $board_id)->where('status', 'open')->where('priority', 'medium')->count(); $low = Task::where('board_id', $board_id)->where('status', 'open')->where('priority', 'low')->count(); $reportData = [ 'completion' => [ 'completed' => $completedTaskCount, 'incomplete' => $openTaskCount, 'overdue' => $overDue, 'total' => $totalTasks ], 'priority' => [ 'high' => $high, 'medium' => $medium, 'low' => $low ] ]; return $reportData; } public function getStageWiseBoardReports($board_id) { $stages = Stage::where('board_id', $board_id) ->where('type', 'stage') ->whereNull('archived_at') ->get(); foreach ($stages as $stage) { $completedTaskCount = Task::where('stage_id', $stage->id) ->where('status', 'closed') ->count(); $openTaskCount = Task::where('stage_id', $stage->id) ->whereNull('due_at') ->where('status', 'open') ->count(); $overDue = Task::where('stage_id', $stage->id) ->whereNotNull('due_at') ->where('status', 'open') ->overdue(true) ->count(); $stage->report = [ 'completed' => $completedTaskCount, 'incomplete' => $openTaskCount, 'overdue' => $overDue ]; } return $stages; } }