comments()->whereNull('parent_id') ->with(['user']); if ($filter == 'oldest') { $commentsQuery = $commentsQuery->oldest(); } else { // latest or newest $commentsQuery = $commentsQuery->latest(); } $comments = $commentsQuery->paginate($per_page); foreach ($comments as $comment) { $comment->replies = $this->getReplies($comment); $comment->replies_count = count($comment->replies); $comment->load('images'); } return $comments; } public function getTotal($id) { $task = Task::findOrFail($id); $totalComment = Comment::where('task_id', $task->id) ->type('comment') ->count(); $totalReply = Comment::where('task_id', $task->id) ->type('reply') ->count(); return $totalComment + $totalReply; } public function getReplies($comment) { $replies = Comment::where('parent_id', $comment->id)->with(['user'])->get(); return $replies; } public function create($commentData, $id) { $comment = Comment::create($commentData); do_action('fluent_boards/comment_created', $comment); return $comment; } private function startsWithAt($word) { return strpos($word, '@') === 0; } public function processMentionAndLink($commentDescription, $mentionData) { // Splitting a string by either a space (" ") or a new line ("\n") $lines = preg_split('/\R/', $commentDescription); // \R matches any kind of line break $mentionedUsernames = []; foreach ($mentionData as $mentionedId) { $user = get_userdata($mentionedId); $mentionedUsernames[$user->user_login] = ['user_id' => $user->ID, 'display_name' => $user->display_name]; } foreach ($lines as &$line) { $words = preg_split('/[ ]+/', $line); foreach ($words as $index => $word) { if ($this->startsWithAt($word)) { $username = substr($word, 1); if (array_key_exists($username, $mentionedUsernames)) { $words[$index] = '' . $mentionedUsernames[$username]['display_name'] . ''; } } elseif ($this->isValidUrl(wp_kses_post($word))) { $words[$index] = ''. esc_url($word). ''; } } // Rejoin the words in this line $line = implode(' ', $words); } // Rejoin the lines, adding back the new line character return implode("\n", $lines); } private function isValidUrl($url) { if (filter_var($url, FILTER_VALIDATE_URL) === false) { return false; } // Additional validation with a regular expression $regex = "/\b(?:https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]/i"; return preg_match($regex, $url); } public function checkIfCommentHaveLinks($comment) { $lines = preg_split('/\R/', $comment); // Split by any kind of line break $commentHasLinks = false; foreach ($lines as &$line) { $words = preg_split('/[ ]+/', $line); // Split by spaces within each line foreach ($words as $index => $word) { $cleanWord = wp_kses_post($word); if ($this->isValidUrl($cleanWord)) { $commentHasLinks = true; $words[$index] = '' . esc_url($cleanWord) . ''; } } // Rejoin words in this line $line = implode(' ', $words); } if ($commentHasLinks) { return implode("\n", $lines); // Rejoin lines with new lines preserved } else { return $comment; // Return original comment if no links were found } } public function attachCommentImages($comment, $imageIds) { foreach ($imageIds as $imageId) { $attachmentObject = CommentImage::findOrFail($imageId); if($attachmentObject) { if ($attachmentObject->object_id == $comment->id && $attachmentObject->object_type == Constant::COMMENT_IMAGE) { continue; } $attachmentObject->object_id = $comment->id; $attachmentObject->object_type = Constant::COMMENT_IMAGE; $attachmentObject->save(); } } //if(in_array("banana", $imageIds)) $commentImages = CommentImage::where('object_id', $comment->id)->where('object_type', Constant::COMMENT_IMAGE)->get(); foreach ($commentImages as $commentImage) { if(!in_array($commentImage->id, $imageIds)) { $commentImage->delete(); } } } public function update($commentData, $comment_id, $mentionData) { $comment = Comment::findOrFail($comment_id); if ($comment->created_by != get_current_user_id()) { return false; } $allMentionedIds = array_merge($comment->settings['mentioned_id'] ?? [], $mentionData ?? []); if ($allMentionedIds) { $processedDescription = $this->processMentionAndLink($commentData['description'], $allMentionedIds); } else { $processedDescription = $this->checkIfCommentHaveLinks($commentData['description']); } $oldComment = $comment->settings['raw_description'] ?? $comment->description; $comment->description = $processedDescription; if($comment->settings != null) { $tempSettings = $comment->settings; $tempSettings['raw_description'] = $commentData['description']; $tempSettings['mentioned_id'] = $allMentionedIds; $comment->settings = $tempSettings; } else { $comment->settings = [ 'raw_description' => $commentData['raw_description'], 'mentioned_id' => $allMentionedIds ]; } $comment->save(); if(!$comment->parent_id) { do_action('fluent_boards/comment_updated', $comment, $oldComment); } return $comment; } public function delete($comment_id) { $comment = Comment::findOrFail($comment_id); $taskId = $comment->task_id; if ($comment->created_by != get_current_user_id()) { return false; } $deleted = $comment->delete(); if ($deleted) { $this->relatedReplyDelete($comment_id); $comment->images()->delete(); $task = Task::findOrFail($taskId); $task->comments_count = $task->comments_count - 1; $task->save(); } do_action('fluent_boards/comment_deleted', $comment); } public function relatedReplyDelete($comment_id) { $replies = Comment::where('parent_id', $comment_id) ->type('reply') ->get(); foreach ($replies as $reply) { $reply->delete(); } } public function updateReply($replyData, $id) { $reply = Comment::findOrFail($id); if ($reply->created_by != get_current_user_id()) { return false; } $oldReply = $reply->description; $reply->description = $replyData['description']; $reply->save(); // do_action('fluent_boards/task_comment_updated', $comment->task_id, $oldComment, $comment->description); return $reply; } public function deleteReply($id) { $reply = Comment::findOrFail($id); // $taskId = $reply->task_id; if ($reply->created_by != get_current_user_id()) { return false; } $reply->delete(); // do_action('fluent_boards/comment_deleted', $taskId); } /** * Adds a task attachment to the specified task. * * @param int $taskId The ID of the task to which the attachment is added. * @param string $title The title of the attachment. * @param string $url The URL of the attachment. * * @return Attachment The updated list of task attachments. * @throws \Exception */ public function createCommentImage($data, $boardId) { /* * I will refactor this function later- within March 2024 Last Week */ $initialDataData = [ 'type' => 'url', 'url' => '', 'name' => '', 'size' => 0, ]; $attachData = array_merge($initialDataData, $data); $UrlMeta = []; if($attachData['type'] == 'url') { $UrlMeta = RemoteUrlParser::parse($attachData['url']); } $attachment = new CommentImage(); $attachment->object_id = 0; $attachment->object_type = Constant::COMMENT_IMAGE; $attachment->attachment_type = $attachData['type']; $attachment->title = $this->setTitle($attachData['type'], $attachData['name'], $UrlMeta); $attachment->file_path = $attachData['type'] != 'url' ? $attachData['file'] : null; $attachment->full_url = esc_url($attachData['url']); $attachment->file_size = $attachData['size']; $attachment->settings = $attachData['type'] == 'url' ? [ 'meta' => $UrlMeta ] : ''; $attachment->driver = 'local'; $attachment->save(); $attachment->public_url = $this->createPublicUrl($attachment, $boardId); return $attachment; } private function createPublicUrl($attachment, $boardId) { return add_query_arg([ 'fbs' => 1, 'fbs_type' => 'public_url', 'fbs_bid' => $boardId, 'fbs_comment_image' => $attachment->file_hash ], site_url('/index.php')); } private function setTitle($type, $title, $UrlMeta) { if($type != 'url') { return sanitize_file_name($title); } return $title ?? $UrlMeta['title'] ?? ''; } }