PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.91.6
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.91.6
2.1.0 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 All 42 releases
fluent-boards / app / Http / Controllers / CommentController.php

CommentController.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.91.6, at app/Http/Controllers/CommentController.php

363 lines 13.5 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\Http\Controllers;
4
5 use FluentBoards\App\Models\Comment;
6 use FluentBoards\App\Models\Task;
7 use FluentBoards\App\Models\User;
8 use FluentBoards\App\Services\NotificationService;
9 use FluentBoards\App\Services\Constant;
10 use FluentBoards\App\Services\Helper;
11 use FluentBoards\App\Services\UploadService;
12 use FluentBoards\Framework\Http\Request\Request;
13 use FluentBoards\App\Services\CommentService;
14 use FluentBoardsPro\App\Services\AttachmentService;
15
16 class CommentController extends Controller
17 {
18 private $commentService;
19 private $notificationService;
20
21 public function __construct(CommentService $commentService, NotificationService $notificationService)
22 {
23 parent::__construct();
24 $this->commentService = $commentService;
25 $this->notificationService = $notificationService;
26 }
27
28 public function getComments(Request $request, $board_id, $task_id)
29 {
30 $board_id = absint($board_id);
31 $task_id = absint($task_id);
32 try {
33 Task::where('board_id', $board_id)->where('id', $task_id)->firstOrFail();
34 $filter = $request->getSafe('filter', 'sanitize_text_field');
35 $per_page = 10;
36
37 $comments = $this->commentService->getComments($task_id, $per_page, $filter);
38 $totalComments = $this->commentService->getTotal($task_id);
39
40 return $this->sendSuccess([
41 'comments' => $comments,
42 'total' => $totalComments
43 ], 200);
44 } catch (\Exception $e) {
45 return $this->sendError($e->getMessage(), 404);
46 }
47 }
48
49 /*
50 * handles comment or reply creation
51 * @param $board_id int
52 * @param $task_id int
53 * @return json
54 */
55 public function create(Request $request, $board_id, $task_id)
56 {
57 // TODO: Refactor the whole request and sanitize process here.. minimize the code in this functions.
58 $requestData = [
59 'parent_id' => $request->getSafe('parent_id', function ($value) {
60 return (empty($value)) ? null : intval( $value);
61 }, null),
62 'description' => $request->getSafe('comment', 'sanitize_textarea_field'),
63 'created_by' => $request->getSafe('comment_by', 'intval', get_current_user_id()),
64 'task_id' => (int) $task_id,
65 'type' => $request->getSafe('comment_type', 'sanitize_text_field', 'comment'),
66 'board_id' => (int) $board_id,
67 ];
68 $validationRules = [
69 'description' => 'required|string',
70 'created_by' => 'required|integer',
71 'board_id' => 'required|integer',
72 'task_id' => 'required|integer',
73 'type' => 'required|string'
74 ];
75
76 $images = $request->getSafe('images');
77 if ($images) {
78 $validationRules['description'] = 'nullable|string';
79 }
80
81 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
82
83
84 try {
85
86 $rawDescription = $commentData['description'];
87 // Sanitize mentionData array to integers
88 $mentionData = [];
89 $rawMentionData = $request->getSafe('mentionData');
90 if ($rawMentionData && is_array($rawMentionData)) {
91 $mentionData = array_filter(array_map('intval', $rawMentionData));
92 }
93 $commentData['settings'] = [ 'raw_description' => $rawDescription, 'mentioned_id' => $mentionData ];
94
95 // Ensure UTF-8 encoding for comment description
96 $description = mb_convert_encoding($commentData['description'], 'UTF-8', 'auto');
97
98 if(!empty($mentionData)) {
99 // Process mentions and links with UTF-8 support
100 $commentData['description'] = $this->commentService->processMentionAndLink($description, $mentionData);
101 } else {
102 // Process links with UTF-8 support
103 $commentData['description'] = $this->commentService->checkIfCommentHaveLinks($description);
104 }
105
106 $comment = $this->commentService->create($commentData, $task_id);
107 $comment['user'] = $comment->user;
108
109 $usersToSendEmail = [];
110 if ($comment->type == 'reply') {
111 $parentComment = Comment::where('board_id', (int) $board_id)->where('id', $comment->parent_id)->firstOrFail();
112 $commenterId = $parentComment->created_by;
113 if ($commenterId != get_current_user_id())
114 {
115 $commenter = User::select('user_email')->findOrFail($commenterId);
116 $commenterEmail = $commenter->user_email;
117 $usersToSendEmail[] = $commenterEmail;
118 }
119 $this->sendMailAfterComment($comment->id, $usersToSendEmail);
120 } else {
121 //sending emails to assignees who enabled their email
122 $usersToSendEmail = $this->notificationService->filterAssigneeToSendEmail($task_id, Constant::BOARD_EMAIL_COMMENT);
123 $this->sendMailAfterComment($comment->id, $usersToSendEmail);
124 }
125
126 if(!empty($mentionData))
127 {
128 $this->notificationService->mentionInComment($comment, $mentionData);
129 }
130
131 $images = $request->getSafe('images');
132 if ($images) {
133 // Sanitize images array to integers
134 $imageIds = [];
135 if (is_array($images)) {
136 $imageIds = array_filter(array_map('intval', $images));
137 }
138 if (!empty($imageIds)) {
139 $this->commentService->attachCommentImages($comment, $imageIds);
140 $comment->load(['images']);
141 }
142 }
143
144 if ($comment->type == 'comment')
145 {
146 $comment->load('replies');
147 }
148
149 return $this->sendSuccess([
150 'message' => __('Comment has been added', 'fluent-boards'),
151 'comment' => $comment
152 ], 201);
153 } catch (\Exception $e) {
154 return $this->sendError($e->getMessage(), 400);
155 }
156 }
157
158 public function update(Request $request, $board_id, $comment_id)
159 {
160 $requestData = [
161 'description' => $request->getSafe('comment', 'sanitize_textarea_field')
162 ];
163
164 $validationRules = [
165 'description' => 'required|string'
166 ];
167
168 $images = $request->getSafe('images');
169 if ($images) {
170 $validationRules['description'] = 'nullable|string';
171 }
172
173 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
174
175 try {
176 // Sanitize mentionData array to integers
177 $mentionData = [];
178 $rawMentionData = $request->getSafe('mentionData');
179 if ($rawMentionData && is_array($rawMentionData)) {
180 $mentionData = array_filter(array_map('intval', $rawMentionData));
181 }
182
183 $comment = $this->commentService->update($commentData, $comment_id, $mentionData);
184
185 if (!$comment) {
186 $errorMessage = __('Unauthorized Action', 'fluent-boards');
187 return $this->sendError($errorMessage, 401);
188 }
189
190 if(!empty($mentionData))
191 {
192 $this->notificationService->mentionInComment($comment, $mentionData);
193 }
194
195 // Sanitize images array to integers
196 $rawImages = $request->getSafe('images');
197 if ($rawImages && is_array($rawImages)) {
198 $imageIds = array_filter(array_map('intval', $rawImages));
199 if (!empty($imageIds)) {
200 $this->commentService->attachCommentImages($comment, $imageIds);
201 $comment->load(['images']);
202 }
203 }
204
205 $comment->load('user');
206
207 return $this->sendSuccess([
208 'comment' => $comment,
209 'message' => __('Comment has been updated', 'fluent-boards'),
210 ], 200);
211 } catch (\Exception $e) {
212 return $this->sendError($e->getMessage(), 404);
213 }
214 }
215
216 public function deleteComment($board_id, $comment_id)
217 {
218 $board_id = absint($board_id);
219 $comment_id = absint($comment_id);
220 try {
221 Comment::where('board_id', $board_id)->where('id', $comment_id)->firstOrFail();
222 $this->commentService->delete($comment_id);
223
224 return $this->sendSuccess([
225 'message' => __('Comment has been deleted', 'fluent-boards'),
226 ], 200);
227 } catch (\Exception $e) {
228 return $this->sendError($e->getMessage(), 404);
229 }
230 }
231
232 public function updateReply(Request $request, $board_id, $reply_id)
233 {
234 $requestData = [
235 'description' => $request->getSafe('comment', 'sanitize_textarea_field')
236 ];
237
238 $validationRules = [
239 'description' => 'required|string'
240 ];
241
242 $replyData = $this->commentSanitizeAndValidate($requestData, $validationRules);
243
244 try {
245 // Sanitize mentionData array to integers
246 $mentionData = [];
247 $rawMentionData = $request->getSafe('mentionData');
248 if ($rawMentionData && is_array($rawMentionData)) {
249 $mentionData = array_filter(array_map('intval', $rawMentionData));
250 }
251
252 $reply = $this->commentService->update($replyData, $reply_id, $mentionData);
253
254 if (!$reply) {
255 $errorMessage = __('Unauthorized Action', 'fluent-boards');
256 return $this->sendError($errorMessage, 401);
257 }
258
259 return $this->sendSuccess([
260 'description' => $reply->description,
261 'message' => __('Reply has been updated', 'fluent-boards'),
262 ], 200);
263 } catch (\Exception $e) {
264 return $this->sendError($e->getMessage(), 404);
265 }
266 }
267
268 public function deleteReply($board_id, $reply_id)
269 {
270 $board_id = absint($board_id);
271 $reply_id = absint($reply_id);
272 try {
273 Comment::where('board_id', $board_id)->where('id', $reply_id)->firstOrFail();
274 $this->commentService->deleteReply($reply_id);
275
276 return $this->sendSuccess([
277 'message' => __('Reply has been deleted', 'fluent-boards'),
278 ], 200);
279 } catch (\Exception $e) {
280 return $this->sendError($e->getMessage(), 404);
281 }
282 }
283
284 public function sendMailAfterComment($commentId, $usersToSendEmail)
285 {
286 $current_user_id = get_current_user_id();
287
288 /* this will run in background as soon as possible */
289 /* sending Model or Model Instance won't work here */
290 as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $usersToSendEmail, $current_user_id], 'fluent-boards');
291 }
292
293 private function commentSanitizeAndValidate($data, array $rules = [])
294 {
295 $data = Helper::sanitizeComment($data);
296
297 return $this->validate($data, $rules);
298 }
299
300 public function handleImageUpload(Request $request, $board_id, $task_id)
301 {
302 $allowedTypes = implode(',', [
303 "image/jpeg",
304 "image/gif",
305 "image/png",
306 "image/bmp",
307 "image/tiff",
308 "image/webp",
309 "image/avif",
310 "image/x-icon",
311 "image/heic",
312 ]);
313
314 $files = $this->validate($request->files(), [
315 'file' => 'mimetypes:' . $allowedTypes,
316 ], [
317 'file.mimetypes' => __('The file must be a image type.', 'fluent-boards')
318 ]);
319
320 $uploadInfo = UploadService::handleFileUpload( $files, $board_id);
321
322 $imageData = $uploadInfo[0];
323 $attachment = $this->commentService->createCommentImage($imageData, $board_id);
324 if(!!defined('FLUENT_BOARDS_PRO_VERSION')) {
325 $mediaData = (new AttachmentService())->processMediaData($imageData, $files['file']);
326 $attachment['driver'] = $mediaData['driver'];
327 $attachment['file_path'] = $mediaData['file_path'];
328 $attachment['full_url'] = $mediaData['full_url'];
329 $attachment->save();
330 }
331 $attachment->public_url = $this->commentService->createPublicUrl($attachment, $board_id);
332
333 return $this->sendSuccess([
334 'message' => __('attachment has been added', 'fluent-boards'),
335 'imageAttachment' => $attachment
336 ], 200);
337
338 }
339
340 public function updateCommentPrivacy($board_id, $comment_id)
341 {
342 $board_id = absint($board_id);
343 $comment_id = absint($comment_id);
344 $comment = Comment::where('board_id', $board_id)->where('id', $comment_id)->firstOrFail();
345
346 // Check if user has permission to update the comment
347 if ($comment->created_by != get_current_user_id()) {
348 return $this->sendError(__('Unauthorized Action', 'fluent-boards'), 401);
349 }
350
351 // Toggle privacy
352 $comment->privacy = ($comment->privacy === 'public') ? 'private' : 'public';
353 $comment->save();
354
355 return $this->sendSuccess([
356 'comment' => $comment,
357 $privacy = $comment->privacy == 'public' ? __('public', 'fluent-boards') : __('private', 'fluent-boards'),
358 // translators: %s is the privacy setting (public or private)
359 'message' => sprintf(__('This comment is now %s', 'fluent-boards'), $privacy),
360 ], 200);
361 }
362 }
363