| 1 |
<?php |
| 2 |
|
| 3 |
namespace FluentBoards\App\Services; |
| 4 |
|
| 5 |
use FluentBoards\App\Models\Comment; |
| 6 |
use FluentBoards\App\Models\CommentImage; |
| 7 |
use FluentBoards\App\Models\Task; |
| 8 |
use FluentBoards\App\Models\TaskActivity; |
| 9 |
use FluentBoardsPro\App\Services\AttachmentService; |
| 10 |
use FluentBoardsPro\App\Services\RemoteUrlParser; |
| 11 |
|
| 12 |
class CommentService |
| 13 |
{ |
| 14 |
public function getComments($id, $per_page, $filter) |
| 15 |
{ |
| 16 |
$task = Task::findOrFail($id); |
| 17 |
|
| 18 |
$commentsQuery = $task->comments()->whereNull('parent_id') |
| 19 |
->with(['user']); |
| 20 |
|
| 21 |
if ($filter == 'oldest') { |
| 22 |
$commentsQuery = $commentsQuery->oldest(); |
| 23 |
} else { // latest or newest |
| 24 |
$commentsQuery = $commentsQuery->latest(); |
| 25 |
} |
| 26 |
$comments = $commentsQuery->paginate($per_page); |
| 27 |
|
| 28 |
foreach ($comments as $comment) { |
| 29 |
$comment->replies = $this->getReplies($comment); |
| 30 |
$comment->replies_count = count($comment->replies); |
| 31 |
$comment->load('images'); |
| 32 |
} |
| 33 |
|
| 34 |
return $comments; |
| 35 |
} |
| 36 |
|
| 37 |
public function getTotal($id) |
| 38 |
{ |
| 39 |
$task = Task::findOrFail($id); |
| 40 |
$totalComment = Comment::where('task_id', $task->id) |
| 41 |
->type('comment') |
| 42 |
->count(); |
| 43 |
$totalReply = Comment::where('task_id', $task->id) |
| 44 |
->type('reply') |
| 45 |
->count(); |
| 46 |
|
| 47 |
return $totalComment + $totalReply; |
| 48 |
} |
| 49 |
|
| 50 |
public function getReplies($comment) |
| 51 |
{ |
| 52 |
$replies = Comment::where('parent_id', $comment->id)->with(['user'])->get(); |
| 53 |
return $replies; |
| 54 |
} |
| 55 |
|
| 56 |
public function create($commentData, $id) |
| 57 |
{ |
| 58 |
$comment = Comment::create($commentData); |
| 59 |
do_action('fluent_boards/comment_created', $comment); |
| 60 |
return $comment; |
| 61 |
} |
| 62 |
|
| 63 |
private function startsWithAt($word) { |
| 64 |
return strpos($word, '@') === 0; |
| 65 |
} |
| 66 |
|
| 67 |
public function processMentionAndLink($commentDescription, $mentionData) |
| 68 |
{ |
| 69 |
// Splitting a string by either a space (" ") or a new line ("\n") |
| 70 |
$lines = preg_split('/\R/', $commentDescription); // \R matches any kind of line break |
| 71 |
|
| 72 |
$mentionedUsernames = []; |
| 73 |
foreach ($mentionData as $mentionedId) { |
| 74 |
$user = get_userdata($mentionedId); |
| 75 |
$mentionedUsernames[$user->user_login] = ['user_id' => $user->ID, 'display_name' => $user->display_name]; |
| 76 |
} |
| 77 |
|
| 78 |
foreach ($lines as &$line) { |
| 79 |
$words = preg_split('/[ ]+/', $line); |
| 80 |
foreach ($words as $index => $word) { |
| 81 |
if ($this->startsWithAt($word)) { |
| 82 |
$username = substr($word, 1); |
| 83 |
if (array_key_exists($username, $mentionedUsernames)) { |
| 84 |
$words[$index] = '<a class="fbs_mention" href="' . fluent_boards_page_url() . 'member/' . $mentionedUsernames[$username]['user_id'] . '/tasks">' . $mentionedUsernames[$username]['display_name'] . '</a>'; |
| 85 |
} |
| 86 |
} elseif ($this->isValidUrl(wp_kses_post($word))) { |
| 87 |
$words[$index] = '<a class="fbs_link" target="_blank" href="'. esc_url($word). '">'. esc_url($word). '</a>'; |
| 88 |
} |
| 89 |
} |
| 90 |
// Rejoin the words in this line |
| 91 |
$line = implode(' ', $words); |
| 92 |
} |
| 93 |
|
| 94 |
// Rejoin the lines, adding back the new line character |
| 95 |
return implode("\n", $lines); |
| 96 |
|
| 97 |
} |
| 98 |
|
| 99 |
private function isValidUrl($url) { |
| 100 |
if (filter_var($url, FILTER_VALIDATE_URL) === false) { |
| 101 |
return false; |
| 102 |
} |
| 103 |
|
| 104 |
// Additional validation with a regular expression |
| 105 |
$regex = "/\b(?:https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]/i"; |
| 106 |
|
| 107 |
return preg_match($regex, $url); |
| 108 |
} |
| 109 |
|
| 110 |
public function checkIfCommentHaveLinks($comment) |
| 111 |
{ |
| 112 |
$lines = preg_split('/\R/', $comment); // Split by any kind of line break |
| 113 |
$commentHasLinks = false; |
| 114 |
|
| 115 |
foreach ($lines as &$line) { |
| 116 |
$words = preg_split('/[ ]+/', $line); // Split by spaces within each line |
| 117 |
foreach ($words as $index => $word) { |
| 118 |
$cleanWord = wp_kses_post($word); |
| 119 |
if ($this->isValidUrl($cleanWord)) { |
| 120 |
$commentHasLinks = true; |
| 121 |
$words[$index] = '<a class="fbs_link" target="_blank" href="' . esc_url($cleanWord) . '">' . esc_url($cleanWord) . '</a>'; |
| 122 |
} |
| 123 |
} |
| 124 |
// Rejoin words in this line |
| 125 |
$line = implode(' ', $words); |
| 126 |
} |
| 127 |
|
| 128 |
if ($commentHasLinks) { |
| 129 |
return implode("\n", $lines); // Rejoin lines with new lines preserved |
| 130 |
} else { |
| 131 |
return $comment; // Return original comment if no links were found |
| 132 |
} |
| 133 |
} |
| 134 |
|
| 135 |
public function attachCommentImages($comment, $imageIds) |
| 136 |
{ |
| 137 |
|
| 138 |
foreach ($imageIds as $imageId) |
| 139 |
{ |
| 140 |
$attachmentObject = CommentImage::findOrFail($imageId); |
| 141 |
if($attachmentObject) { |
| 142 |
if ($attachmentObject->object_id == $comment->id && $attachmentObject->object_type == Constant::COMMENT_IMAGE) { |
| 143 |
continue; |
| 144 |
} |
| 145 |
$attachmentObject->object_id = $comment->id; |
| 146 |
$attachmentObject->object_type = Constant::COMMENT_IMAGE; |
| 147 |
$attachmentObject->save(); |
| 148 |
} |
| 149 |
} |
| 150 |
//if(in_array("banana", $imageIds)) |
| 151 |
$commentImages = CommentImage::where('object_id', $comment->id)->where('object_type', Constant::COMMENT_IMAGE)->get(); |
| 152 |
|
| 153 |
foreach ($commentImages as $commentImage) { |
| 154 |
if(!in_array($commentImage->id, $imageIds)) { |
| 155 |
$commentImage->delete(); |
| 156 |
} |
| 157 |
} |
| 158 |
} |
| 159 |
|
| 160 |
public function update($commentData, $comment_id, $mentionData) |
| 161 |
{ |
| 162 |
$comment = Comment::findOrFail($comment_id); |
| 163 |
|
| 164 |
if ($comment->created_by != get_current_user_id()) { |
| 165 |
return false; |
| 166 |
} |
| 167 |
|
| 168 |
$allMentionedIds = array_merge($comment->settings['mentioned_id'] ?? [], $mentionData ?? []); |
| 169 |
|
| 170 |
if ($allMentionedIds) { |
| 171 |
$processedDescription = $this->processMentionAndLink($commentData['description'], $allMentionedIds); |
| 172 |
} else { |
| 173 |
$processedDescription = $this->checkIfCommentHaveLinks($commentData['description']); |
| 174 |
} |
| 175 |
|
| 176 |
$oldComment = $comment->settings['raw_description'] ?? $comment->description; |
| 177 |
$comment->description = $processedDescription; |
| 178 |
|
| 179 |
if($comment->settings != null) |
| 180 |
{ |
| 181 |
$tempSettings = $comment->settings; |
| 182 |
$tempSettings['raw_description'] = $commentData['description']; |
| 183 |
$tempSettings['mentioned_id'] = $allMentionedIds; |
| 184 |
$comment->settings = $tempSettings; |
| 185 |
} else { |
| 186 |
$comment->settings = [ |
| 187 |
'raw_description' => $commentData['raw_description'], |
| 188 |
'mentioned_id' => $allMentionedIds |
| 189 |
]; |
| 190 |
} |
| 191 |
$comment->save(); |
| 192 |
|
| 193 |
if(!$comment->parent_id) { |
| 194 |
do_action('fluent_boards/comment_updated', $comment, $oldComment); |
| 195 |
} |
| 196 |
|
| 197 |
return $comment; |
| 198 |
} |
| 199 |
|
| 200 |
public function delete($comment_id) |
| 201 |
{ |
| 202 |
$comment = Comment::findOrFail($comment_id); |
| 203 |
$taskId = $comment->task_id; |
| 204 |
|
| 205 |
if ($comment->created_by != get_current_user_id()) { |
| 206 |
return false; |
| 207 |
} |
| 208 |
|
| 209 |
$deleted = $comment->delete(); |
| 210 |
|
| 211 |
if ($deleted) { |
| 212 |
$this->relatedReplyDelete($comment_id); |
| 213 |
$comment->images()->delete(); |
| 214 |
$task = Task::findOrFail($taskId); |
| 215 |
$task->comments_count = $task->comments_count - 1; |
| 216 |
$task->save(); |
| 217 |
} |
| 218 |
|
| 219 |
do_action('fluent_boards/comment_deleted', $comment); |
| 220 |
} |
| 221 |
|
| 222 |
public function relatedReplyDelete($comment_id) |
| 223 |
{ |
| 224 |
$replies = Comment::where('parent_id', $comment_id) |
| 225 |
->type('reply') |
| 226 |
->get(); |
| 227 |
foreach ($replies as $reply) { |
| 228 |
$reply->delete(); |
| 229 |
} |
| 230 |
} |
| 231 |
|
| 232 |
public function updateReply($replyData, $id) |
| 233 |
{ |
| 234 |
$reply = Comment::findOrFail($id); |
| 235 |
|
| 236 |
if ($reply->created_by != get_current_user_id()) { |
| 237 |
return false; |
| 238 |
} |
| 239 |
|
| 240 |
$oldReply = $reply->description; |
| 241 |
$reply->description = $replyData['description']; |
| 242 |
$reply->save(); |
| 243 |
// do_action('fluent_boards/task_comment_updated', $comment->task_id, $oldComment, $comment->description); |
| 244 |
|
| 245 |
return $reply; |
| 246 |
} |
| 247 |
|
| 248 |
public function deleteReply($id) |
| 249 |
{ |
| 250 |
$reply = Comment::findOrFail($id); |
| 251 |
// $taskId = $reply->task_id; |
| 252 |
|
| 253 |
if ($reply->created_by != get_current_user_id()) { |
| 254 |
return false; |
| 255 |
} |
| 256 |
|
| 257 |
$reply->delete(); |
| 258 |
|
| 259 |
// do_action('fluent_boards/comment_deleted', $taskId); |
| 260 |
} |
| 261 |
|
| 262 |
/** |
| 263 |
* Adds a task attachment to the specified task. |
| 264 |
* |
| 265 |
* @param int $taskId The ID of the task to which the attachment is added. |
| 266 |
* @param string $title The title of the attachment. |
| 267 |
* @param string $url The URL of the attachment. |
| 268 |
* |
| 269 |
* @return Attachment The updated list of task attachments. |
| 270 |
* @throws \Exception |
| 271 |
*/ |
| 272 |
public function createCommentImage($data, $boardId) |
| 273 |
{ |
| 274 |
/* |
| 275 |
* I will refactor this function later- within March 2024 Last Week |
| 276 |
*/ |
| 277 |
$initialDataData = [ |
| 278 |
'type' => 'url', |
| 279 |
'url' => '', |
| 280 |
'name' => '', |
| 281 |
'size' => 0, |
| 282 |
]; |
| 283 |
|
| 284 |
$attachData = array_merge($initialDataData, $data); |
| 285 |
$UrlMeta = []; |
| 286 |
if($attachData['type'] == 'url') { |
| 287 |
$UrlMeta = RemoteUrlParser::parse($attachData['url']); |
| 288 |
} |
| 289 |
$attachment = new CommentImage(); |
| 290 |
$attachment->object_id = 0; |
| 291 |
$attachment->object_type = Constant::COMMENT_IMAGE; |
| 292 |
$attachment->attachment_type = $attachData['type']; |
| 293 |
$attachment->title = $this->setTitle($attachData['type'], $attachData['name'], $UrlMeta); |
| 294 |
$attachment->file_path = $attachData['type'] != 'url' ? $attachData['file'] : null; |
| 295 |
$attachment->full_url = esc_url($attachData['url']); |
| 296 |
$attachment->file_size = $attachData['size']; |
| 297 |
$attachment->settings = $attachData['type'] == 'url' ? [ |
| 298 |
'meta' => $UrlMeta |
| 299 |
] : ''; |
| 300 |
$attachment->driver = 'local'; |
| 301 |
$attachment->save(); |
| 302 |
|
| 303 |
return $attachment; |
| 304 |
} |
| 305 |
|
| 306 |
public function createPublicUrl($attachment, $boardId) |
| 307 |
{ |
| 308 |
return add_query_arg([ |
| 309 |
'fbs' => 1, |
| 310 |
'fbs_type' => 'public_url', |
| 311 |
'fbs_bid' => $boardId, |
| 312 |
'fbs_comment_image' => $attachment->file_hash |
| 313 |
], site_url('/index.php')); |
| 314 |
} |
| 315 |
|
| 316 |
private function setTitle($type, $title, $UrlMeta) |
| 317 |
{ |
| 318 |
if($type != 'url') { |
| 319 |
return sanitize_file_name($title); |
| 320 |
} |
| 321 |
return $title ?? $UrlMeta['title'] ?? ''; |
| 322 |
} |
| 323 |
} |
| 324 |
|