PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.2.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.2.0
2.2.1 2.2.0 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 All 44 releases
← All changes | app/Http/Controllers/CommentController.php +162 -56 1.31 → 2.2.0 View file →
@@ -2,11 +2,9 @@
2 2
3 3 namespace FluentBoards\App\Http\Controllers;
4 4
5 5 use FluentBoards\App\Models\Comment;
6 -use FluentBoards\App\Models\Task;
7 6 use FluentBoards\App\Services\NotificationService;
8 -use FluentBoards\App\Services\Constant;
9 7 use FluentBoards\App\Services\Helper;
10 8 use FluentBoards\App\Services\UploadService;
11 9 use FluentBoards\Framework\Http\Request\Request;
12 10 use FluentBoards\App\Services\CommentService;
@@ -26,13 +24,13 @@
26 24
27 25 public function getComments(Request $request, $board_id, $task_id)
28 26 {
29 27 try {
30 - $filter = $request->getSafe('filter');
28 + $filter = $request->getSafe('filter', 'sanitize_text_field');
31 29 $per_page = 10;
32 30
33 - $comments = $this->commentService->getComments($task_id, $per_page, $filter);
34 - $totalComments = $this->commentService->getTotal($task_id);
31 + $comments = $this->commentService->getComments($task_id, $per_page, $filter, $board_id);
32 + $totalComments = $this->commentService->getTotal($task_id, $board_id);
35 33
36 34 return $this->sendSuccess([
37 35 'comments' => $comments,
38 36 'total' => $totalComments
@@ -51,13 +49,15 @@
51 49 public function create(Request $request, $board_id, $task_id)
52 50 {
53 51 // TODO: Refactor the whole request and sanitize process here.. minimize the code in this functions.
54 52 $requestData = [
55 - 'parent_id' => $request->parent_id,
56 - 'description' => $request->comment,
57 - 'created_by' => $request->comment_by,
58 - 'task_id' => $task_id,
59 - 'type' => $request->comment_type ? $request->comment_type : 'comment',
53 + 'parent_id' => $request->getSafe('parent_id', function ($value) {
54 + return (empty($value)) ? null : intval( $value);
55 + }, null),
56 + 'description' => $this->commentService->sanitizeContent($request->get('comment', '')),
57 + 'created_by' => get_current_user_id(),
58 + 'task_id' => (int) $task_id,
59 + 'type' => $request->getSafe('comment_type', 'sanitize_text_field', 'comment'),
60 60 'board_id' => (int) $board_id,
61 61 ];
62 62 $validationRules = [
63 63 'description' => 'required|string',
@@ -66,9 +66,10 @@
66 66 'task_id' => 'required|integer',
67 67 'type' => 'required|string'
68 68 ];
69 69
70 - if ($request->images) {
70 + $imageIds = $this->getImageIdsFromRequest($request);
71 + if ($imageIds) {
71 72 $validationRules['description'] = 'nullable|string';
72 73 }
73 74
74 75 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
@@ -74,52 +75,63 @@
74 75 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
75 76
76 77
77 78 try {
79 + if (!empty($imageIds)) {
80 + $this->commentService->assertCommentImagesAttachable($imageIds, $board_id, $task_id);
81 + }
82 +
78 83 $rawDescription = $commentData['description'];
79 - $commentData['settings'] = [ 'raw_description' => $rawDescription, 'mentioned_id' => $request->mentionData ];
80 - if($request->mentionData) {
81 - $commentData['description'] = $this->commentService->processMentionAndLink($commentData['description'], $request->mentionData);
84 + $mentionData = $this->getMentionData($request, $board_id);
85 + $commentData['settings'] = [ 'raw_description' => $rawDescription, 'mentioned_id' => $mentionData ];
86 +
87 + $commentData['description'] = $this->commentService->renderContent($rawDescription, $mentionData);
88 +
89 + $comment = $this->commentService->create($commentData, $task_id, $board_id);
90 + if (!empty($imageIds)) {
91 + $this->commentService->attachCommentImages($comment, $imageIds);
92 + $comment->load(['images']);
93 + }
94 + $comment['user'] = $comment->user;
95 +
96 + $recipientUserIds = [];
97 + if ($comment->type == 'reply') {
98 + $parentComment = Comment::findOrFail($comment->parent_id);
99 + $commenterId = $parentComment->created_by;
100 + if ($commenterId != get_current_user_id())
101 + {
102 + $recipientUserIds[] = absint($commenterId);
103 + }
104 + $this->sendMailAfterComment($comment->id, $recipientUserIds);
82 105 } else {
83 - $commentData['description'] = $this->commentService->checkIfCommentHaveLinks($commentData['description']);
106 + // Queue revocable IDs; the worker rechecks membership and preferences before sending.
107 + $recipientUserIds = $this->notificationService->getCommentRecipientUserIds($task_id);
108 + $this->sendMailAfterComment($comment->id, $recipientUserIds);
84 109 }
85 110
86 - $comment = $this->commentService->create($commentData, $task_id);
87 - $comment['user'] = $comment->user;
88 - //sending emails to assignees who enabled their email
89 - $usersToSendEmail = $this->notificationService->filterAssigneeToSendEmail($task_id, Constant::BOARD_EMAIL_COMMENT);
90 -
91 - $this->sendMailAfterComment($comment->id, $usersToSendEmail);
92 -
93 - if($request->mentionData)
111 + if(!empty($mentionData))
94 112 {
95 - $this->notificationService->mentionInComment($comment, $request->mentionData);
113 + $this->notificationService->mentionInComment($comment, $mentionData);
96 114 }
97 115
98 - if($request->images)
116 + if ($comment->type == 'comment')
99 117 {
100 - $this->commentService->attachCommentImages($comment, $request->images);
101 - $comment->load(['images']);
118 + $comment->load('replies');
102 119 }
103 120
104 - $comment->load('replies');
105 -
106 121 return $this->sendSuccess([
107 122 'message' => __('Comment has been added', 'fluent-boards'),
108 123 'comment' => $comment
109 124 ], 201);
110 125 } catch (\Exception $e) {
111 - return $this->sendError($e->getMessage(), 404);
126 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 400);
112 127 }
113 128 }
114 129
115 130 public function update(Request $request, $board_id, $comment_id)
116 131 {
117 -// $commentData = $this->commentSanitizeAndValidate($request->all(), [
118 -// 'description' => 'required|string',
119 -// ]);
120 132 $requestData = [
121 - 'description' => $request->comment
133 + 'description' => $this->commentService->sanitizeContent($request->get('comment', ''))
122 134 ];
123 135
124 136 $validationRules = [
125 137 'description' => 'required|string'
@@ -124,9 +136,11 @@
124 136 $validationRules = [
125 137 'description' => 'required|string'
126 138 ];
127 139
128 - if ($request->images) {
140 + $hasImagesParam = $this->requestHasImagesArray($request);
141 + $imageIds = $this->getImageIdsFromRequest($request);
142 + if ($hasImagesParam) {
129 143 $validationRules['description'] = 'nullable|string';
130 144 }
131 145
132 146 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
@@ -131,24 +145,34 @@
131 145
132 146 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
133 147
134 148 try {
135 - $comment = $this->commentService->update($commentData, $comment_id, $request->mentionData);
149 + if ($hasImagesParam) {
150 + $commentForImages = $this->commentService->findCommentOnBoard($comment_id, $board_id);
151 + if ($commentForImages->created_by != get_current_user_id()) {
152 + $errorMessage = __('Unauthorized Action', 'fluent-boards');
153 + return $this->sendError($errorMessage, 401);
154 + }
155 + $this->commentService->assertCommentImagesAttachableForComment($commentForImages, $imageIds);
156 + }
136 157
137 - if($request->mentionData)
138 - {
139 - $this->notificationService->mentionInComment($comment, $request->mentionData);
158 + $mentionData = $this->getMentionData($request);
159 +
160 + $comment = $this->commentService->update($commentData, $comment_id, $mentionData, $board_id);
161 +
162 + if (!$comment) {
163 + $errorMessage = __('Unauthorized Action', 'fluent-boards');
164 + return $this->sendError($errorMessage, 401);
140 165 }
141 166
142 - if($request->images)
167 + if(!empty($mentionData))
143 168 {
144 - $this->commentService->attachCommentImages($comment, $request->images);
145 - $comment->load(['images']);
169 + $this->notificationService->mentionInComment($comment, $mentionData);
146 170 }
147 171
148 - if ( !$comment ) {
149 - $errorMessage = __('Unauthorized Action', 'fluent-boards');
150 - return $this->sendError($errorMessage, 401);
172 + if ($hasImagesParam) {
173 + $this->commentService->attachCommentImages($comment, $imageIds);
174 + $comment->load(['images']);
151 175 }
152 176
153 177 $comment->load('user');
154 178
@@ -156,16 +180,16 @@
156 180 'comment' => $comment,
157 181 'message' => __('Comment has been updated', 'fluent-boards'),
158 182 ], 200);
159 183 } catch (\Exception $e) {
160 - return $this->sendError($e->getMessage(), 404);
184 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 404);
161 185 }
162 186 }
163 187
164 - public function delete($board_id, $comment_id)
188 + public function deleteComment($board_id, $comment_id)
165 189 {
166 190 try {
167 - $this->commentService->delete($comment_id);
191 + $this->commentService->delete($comment_id, $board_id);
168 192
169 193 return $this->sendSuccess([
170 194 'message' => __('Comment has been deleted', 'fluent-boards'),
171 195 ], 200);
@@ -176,9 +200,9 @@
176 200
177 201 public function updateReply(Request $request, $board_id, $reply_id)
178 202 {
179 203 $requestData = [
180 - 'description' => $request->comment
204 + 'description' => $this->commentService->sanitizeContent($request->get('comment', ''))
181 205 ];
182 206
183 207 $validationRules = [
184 208 'description' => 'required|string'
@@ -186,9 +210,11 @@
186 210
187 211 $replyData = $this->commentSanitizeAndValidate($requestData, $validationRules);
188 212
189 213 try {
190 - $reply = $this->commentService->update($replyData, $reply_id, $request->mentionData);
214 + $mentionData = $this->getMentionData($request);
215 +
216 + $reply = $this->commentService->update($replyData, $reply_id, $mentionData, $board_id);
191 217
192 218 if (!$reply) {
193 219 $errorMessage = __('Unauthorized Action', 'fluent-boards');
194 220 return $this->sendError($errorMessage, 401);
@@ -198,9 +224,9 @@
198 224 'description' => $reply->description,
199 225 'message' => __('Reply has been updated', 'fluent-boards'),
200 226 ], 200);
201 227 } catch (\Exception $e) {
202 - return $this->sendError($e->getMessage(), 404);
228 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 404);
203 229 }
204 230 }
205 231
206 232 public function deleteReply($board_id, $reply_id)
@@ -205,9 +231,9 @@
205 231
206 232 public function deleteReply($board_id, $reply_id)
207 233 {
208 234 try {
209 - $this->commentService->deleteReply($reply_id);
235 + $this->commentService->deleteReply($reply_id, $board_id);
210 236
211 237 return $this->sendSuccess([
212 238 'message' => __('Reply has been deleted', 'fluent-boards'),
213 239 ], 200);
@@ -215,17 +241,51 @@
215 241 return $this->sendError($e->getMessage(), 404);
216 242 }
217 243 }
218 244
219 - public function sendMailAfterComment($commentId, $usersToSendEmail)
245 + public function sendMailAfterComment($commentId, $recipientUserIds)
220 246 {
247 + if (empty($recipientUserIds)) {
248 + return;
249 + }
250 +
221 251 $current_user_id = get_current_user_id();
222 252
223 253 /* this will run in background as soon as possible */
224 254 /* sending Model or Model Instance won't work here */
225 - as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $usersToSendEmail, $current_user_id], 'fluent-boards');
255 + as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $recipientUserIds, $current_user_id], 'fluent-boards');
226 256 }
227 257
258 + /**
259 + * Sanitize mention IDs and optionally verify board membership before a create.
260 + *
261 + * @param Request $request
262 + * @param int $boardId
263 + * @return array
264 + * @throws \Exception
265 + */
266 + private function getMentionData(Request $request, $boardId = null)
267 + {
268 + $rawMentionData = $request->getSafe('mentionData');
269 + if (!is_array($rawMentionData)) {
270 + return [];
271 + }
272 +
273 + $mentionData = array_values(array_unique(array_filter(array_map('absint', $rawMentionData))));
274 +
275 + if (!$boardId) {
276 + return $mentionData;
277 + }
278 +
279 + $boardMemberIds = $this->notificationService->resolveBoardMentionUserIds($boardId, $mentionData);
280 +
281 + if (array_diff($mentionData, $boardMemberIds)) {
282 + throw new \Exception(esc_html__('One or more mentioned users are not members of this board', 'fluent-boards'), 403);
283 + }
284 +
285 + return $boardMemberIds;
286 + }
287 +
228 288 private function commentSanitizeAndValidate($data, array $rules = [])
229 289 {
230 290 $data = Helper::sanitizeComment($data);
231 291
@@ -251,13 +311,22 @@
251 311 ], [
252 312 'file.mimetypes' => __('The file must be a image type.', 'fluent-boards')
253 313 ]);
254 314
315 + (new \FluentBoards\App\Services\TaskService())->findTaskOnBoard($task_id, $board_id);
316 +
255 317 $uploadInfo = UploadService::handleFileUpload( $files, $board_id);
256 318
257 319 $imageData = $uploadInfo[0];
258 -// $attachmentService = new AttachmentService();
259 - $attachment = $this->commentService->createCommentImage($imageData, $board_id);
320 + $attachment = $this->commentService->createCommentImage($imageData, $board_id, $task_id);
321 + if(!!defined('FLUENT_BOARDS_PRO_VERSION')) {
322 + $mediaData = (new AttachmentService())->processMediaData($imageData, $files['file']);
323 + $attachment['driver'] = $mediaData['driver'];
324 + $attachment['file_path'] = $mediaData['file_path'];
325 + $attachment['full_url'] = $mediaData['full_url'];
326 + $attachment->save();
327 + }
328 + $attachment->public_url = $this->commentService->createPublicUrl($attachment, $board_id);
260 329
261 330 return $this->sendSuccess([
262 331 'message' => __('attachment has been added', 'fluent-boards'),
263 332 'imageAttachment' => $attachment
@@ -262,6 +331,43 @@
262 331 'message' => __('attachment has been added', 'fluent-boards'),
263 332 'imageAttachment' => $attachment
264 333 ], 200);
265 334
335 + }
336 +
337 + public function updateCommentPrivacy($board_id, $comment_id)
338 + {
339 + $comment = $this->commentService->findCommentOnBoard($comment_id, $board_id);
340 +
341 + // Check if user has permission to update the comment
342 + if ($comment->created_by != get_current_user_id()) {
343 + return $this->sendError(__('Unauthorized Action', 'fluent-boards'), 401);
344 + }
345 +
346 + // Toggle privacy
347 + $comment->privacy = ($comment->privacy === 'public') ? 'private' : 'public';
348 + $comment->save();
349 +
350 + return $this->sendSuccess([
351 + 'comment' => $comment,
352 + $privacy = $comment->privacy == 'public' ? __('public', 'fluent-boards') : __('private', 'fluent-boards'),
353 + // translators: %s is the privacy setting (public or private)
354 + 'message' => sprintf(__('This comment is now %s', 'fluent-boards'), $privacy),
355 + ], 200);
356 + }
357 +
358 + private function getImageIdsFromRequest(Request $request)
359 + {
360 + $images = $request->getSafe('images');
361 +
362 + if (!$images || !is_array($images)) {
363 + return [];
364 + }
365 +
366 + return array_values(array_filter(array_unique(array_map('intval', $images))));
367 + }
368 +
369 + private function requestHasImagesArray(Request $request)
370 + {
371 + return $request->exists('images') && is_array($request->getSafe('images'));
266 372 }
267 373 }