PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / trunk
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration vtrunk
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 1.45 All 41 releases
← All changes | app/Http/Controllers/CommentController.php +85 -60 2.0.4trunk View file →
@@ -2,12 +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 -use FluentBoards\App\Models\User;
8 6 use FluentBoards\App\Services\NotificationService;
9 -use FluentBoards\App\Services\Constant;
10 7 use FluentBoards\App\Services\Helper;
11 8 use FluentBoards\App\Services\UploadService;
12 9 use FluentBoards\Framework\Http\Request\Request;
13 10 use FluentBoards\App\Services\CommentService;
@@ -56,9 +53,9 @@
56 53 'parent_id' => $request->getSafe('parent_id', function ($value) {
57 54 return (empty($value)) ? null : intval( $value);
58 55 }, null),
59 56 'description' => $request->getSafe('comment', 'sanitize_textarea_field'),
60 - 'created_by' => $request->getSafe('comment_by', 'intval', get_current_user_id()),
57 + 'created_by' => get_current_user_id(),
61 58 'task_id' => (int) $task_id,
62 59 'type' => $request->getSafe('comment_type', 'sanitize_text_field', 'comment'),
63 60 'board_id' => (int) $board_id,
64 61 ];
@@ -69,10 +66,10 @@
69 66 'task_id' => 'required|integer',
70 67 'type' => 'required|string'
71 68 ];
72 69
73 - $images = $request->getSafe('images');
74 - if ($images) {
70 + $imageIds = $this->getImageIdsFromRequest($request);
71 + if ($imageIds) {
75 72 $validationRules['description'] = 'nullable|string';
76 73 }
77 74
78 75 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
@@ -78,16 +75,14 @@
78 75 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
79 76
80 77
81 78 try {
79 + if (!empty($imageIds)) {
80 + $this->commentService->assertCommentImagesAttachable($imageIds, $board_id, $task_id);
81 + }
82 82
83 83 $rawDescription = $commentData['description'];
84 - // Sanitize mentionData array to integers
85 - $mentionData = [];
86 - $rawMentionData = $request->getSafe('mentionData');
87 - if ($rawMentionData && is_array($rawMentionData)) {
88 - $mentionData = array_filter(array_map('intval', $rawMentionData));
89 - }
84 + $mentionData = $this->getMentionData($request, $board_id);
90 85 $commentData['settings'] = [ 'raw_description' => $rawDescription, 'mentioned_id' => $mentionData ];
91 86
92 87 // Ensure UTF-8 encoding for comment description
93 88 $description = mb_convert_encoding($commentData['description'], 'UTF-8', 'auto');
@@ -100,25 +95,27 @@
100 95 $commentData['description'] = $this->commentService->checkIfCommentHaveLinks($description);
101 96 }
102 97
103 98 $comment = $this->commentService->create($commentData, $task_id, $board_id);
99 + if (!empty($imageIds)) {
100 + $this->commentService->attachCommentImages($comment, $imageIds);
101 + $comment->load(['images']);
102 + }
104 103 $comment['user'] = $comment->user;
105 104
106 - $usersToSendEmail = [];
105 + $recipientUserIds = [];
107 106 if ($comment->type == 'reply') {
108 107 $parentComment = Comment::findOrFail($comment->parent_id);
109 108 $commenterId = $parentComment->created_by;
110 109 if ($commenterId != get_current_user_id())
111 110 {
112 - $commenter = User::select('user_email')->findOrFail($commenterId);
113 - $commenterEmail = $commenter->user_email;
114 - $usersToSendEmail[] = $commenterEmail;
111 + $recipientUserIds[] = absint($commenterId);
115 112 }
116 - $this->sendMailAfterComment($comment->id, $usersToSendEmail);
113 + $this->sendMailAfterComment($comment->id, $recipientUserIds);
117 114 } else {
118 - //sending emails to assignees who enabled their email
119 - $usersToSendEmail = $this->notificationService->filterAssigneeToSendEmail($task_id, Constant::BOARD_EMAIL_COMMENT);
120 - $this->sendMailAfterComment($comment->id, $usersToSendEmail);
115 + // Queue revocable IDs; the worker rechecks membership and preferences before sending.
116 + $recipientUserIds = $this->notificationService->getCommentRecipientUserIds($task_id);
117 + $this->sendMailAfterComment($comment->id, $recipientUserIds);
121 118 }
122 119
123 120 if(!empty($mentionData))
124 121 {
@@ -124,21 +121,8 @@
124 121 {
125 122 $this->notificationService->mentionInComment($comment, $mentionData);
126 123 }
127 124
128 - $images = $request->getSafe('images');
129 - if ($images) {
130 - // Sanitize images array to integers
131 - $imageIds = [];
132 - if (is_array($images)) {
133 - $imageIds = array_filter(array_map('intval', $images));
134 - }
135 - if (!empty($imageIds)) {
136 - $this->commentService->attachCommentImages($comment, $imageIds);
137 - $comment->load(['images']);
138 - }
139 - }
140 -
141 125 if ($comment->type == 'comment')
142 126 {
143 127 $comment->load('replies');
144 128 }
@@ -147,9 +131,9 @@
147 131 'message' => __('Comment has been added', 'fluent-boards'),
148 132 'comment' => $comment
149 133 ], 201);
150 134 } catch (\Exception $e) {
151 - return $this->sendError($e->getMessage(), 400);
135 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 400);
152 136 }
153 137 }
154 138
155 139 public function update(Request $request, $board_id, $comment_id)
@@ -161,10 +145,11 @@
161 145 $validationRules = [
162 146 'description' => 'required|string'
163 147 ];
164 148
165 - $images = $request->getSafe('images');
166 - if ($images) {
149 + $hasImagesParam = $this->requestHasImagesArray($request);
150 + $imageIds = $this->getImageIdsFromRequest($request);
151 + if ($hasImagesParam) {
167 152 $validationRules['description'] = 'nullable|string';
168 153 }
169 154
170 155 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
@@ -169,14 +154,18 @@
169 154
170 155 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
171 156
172 157 try {
173 - // Sanitize mentionData array to integers
174 - $mentionData = [];
175 - $rawMentionData = $request->getSafe('mentionData');
176 - if ($rawMentionData && is_array($rawMentionData)) {
177 - $mentionData = array_filter(array_map('intval', $rawMentionData));
158 + if ($hasImagesParam) {
159 + $commentForImages = $this->commentService->findCommentOnBoard($comment_id, $board_id);
160 + if ($commentForImages->created_by != get_current_user_id()) {
161 + $errorMessage = __('Unauthorized Action', 'fluent-boards');
162 + return $this->sendError($errorMessage, 401);
163 + }
164 + $this->commentService->assertCommentImagesAttachableForComment($commentForImages, $imageIds);
178 165 }
166 +
167 + $mentionData = $this->getMentionData($request);
179 168
180 169 $comment = $this->commentService->update($commentData, $comment_id, $mentionData, $board_id);
181 170
182 171 if (!$comment) {
@@ -188,16 +177,11 @@
188 177 {
189 178 $this->notificationService->mentionInComment($comment, $mentionData);
190 179 }
191 180
192 - // Sanitize images array to integers
193 - $rawImages = $request->getSafe('images');
194 - if ($rawImages && is_array($rawImages)) {
195 - $imageIds = array_filter(array_map('intval', $rawImages));
196 - if (!empty($imageIds)) {
197 - $this->commentService->attachCommentImages($comment, $imageIds);
198 - $comment->load(['images']);
199 - }
181 + if ($hasImagesParam) {
182 + $this->commentService->attachCommentImages($comment, $imageIds);
183 + $comment->load(['images']);
200 184 }
201 185
202 186 $comment->load('user');
203 187
@@ -205,9 +189,9 @@
205 189 'comment' => $comment,
206 190 'message' => __('Comment has been updated', 'fluent-boards'),
207 191 ], 200);
208 192 } catch (\Exception $e) {
209 - return $this->sendError($e->getMessage(), 404);
193 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 404);
210 194 }
211 195 }
212 196
213 197 public function deleteComment($board_id, $comment_id)
@@ -235,14 +219,9 @@
235 219
236 220 $replyData = $this->commentSanitizeAndValidate($requestData, $validationRules);
237 221
238 222 try {
239 - // Sanitize mentionData array to integers
240 - $mentionData = [];
241 - $rawMentionData = $request->getSafe('mentionData');
242 - if ($rawMentionData && is_array($rawMentionData)) {
243 - $mentionData = array_filter(array_map('intval', $rawMentionData));
244 - }
223 + $mentionData = $this->getMentionData($request);
245 224
246 225 $reply = $this->commentService->update($replyData, $reply_id, $mentionData, $board_id);
247 226
248 227 if (!$reply) {
@@ -254,9 +233,9 @@
254 233 'description' => $reply->description,
255 234 'message' => __('Reply has been updated', 'fluent-boards'),
256 235 ], 200);
257 236 } catch (\Exception $e) {
258 - return $this->sendError($e->getMessage(), 404);
237 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 404);
259 238 }
260 239 }
261 240
262 241 public function deleteReply($board_id, $reply_id)
@@ -271,17 +250,47 @@
271 250 return $this->sendError($e->getMessage(), 404);
272 251 }
273 252 }
274 253
275 - public function sendMailAfterComment($commentId, $usersToSendEmail)
254 + public function sendMailAfterComment($commentId, $recipientUserIds)
276 255 {
277 256 $current_user_id = get_current_user_id();
278 257
279 258 /* this will run in background as soon as possible */
280 259 /* sending Model or Model Instance won't work here */
281 - as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $usersToSendEmail, $current_user_id], 'fluent-boards');
260 + as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $recipientUserIds, $current_user_id], 'fluent-boards');
282 261 }
283 262
263 + /**
264 + * Sanitize mention IDs and optionally verify board membership before a create.
265 + *
266 + * @param Request $request
267 + * @param int $boardId
268 + * @return array
269 + * @throws \Exception
270 + */
271 + private function getMentionData(Request $request, $boardId = null)
272 + {
273 + $rawMentionData = $request->getSafe('mentionData');
274 + if (!is_array($rawMentionData)) {
275 + return [];
276 + }
277 +
278 + $mentionData = array_values(array_unique(array_filter(array_map('absint', $rawMentionData))));
279 +
280 + if (!$boardId) {
281 + return $mentionData;
282 + }
283 +
284 + $boardMemberIds = $this->notificationService->resolveBoardMentionUserIds($boardId, $mentionData);
285 +
286 + if (array_diff($mentionData, $boardMemberIds)) {
287 + throw new \Exception(esc_html__('One or more mentioned users are not members of this board', 'fluent-boards'), 403);
288 + }
289 +
290 + return $boardMemberIds;
291 + }
292 +
284 293 private function commentSanitizeAndValidate($data, array $rules = [])
285 294 {
286 295 $data = Helper::sanitizeComment($data);
287 296
@@ -312,9 +321,9 @@
312 321
313 322 $uploadInfo = UploadService::handleFileUpload( $files, $board_id);
314 323
315 324 $imageData = $uploadInfo[0];
316 - $attachment = $this->commentService->createCommentImage($imageData, $board_id);
325 + $attachment = $this->commentService->createCommentImage($imageData, $board_id, $task_id);
317 326 if(!!defined('FLUENT_BOARDS_PRO_VERSION')) {
318 327 $mediaData = (new AttachmentService())->processMediaData($imageData, $files['file']);
319 328 $attachment['driver'] = $mediaData['driver'];
320 329 $attachment['file_path'] = $mediaData['file_path'];
@@ -348,6 +357,22 @@
348 357 $privacy = $comment->privacy == 'public' ? __('public', 'fluent-boards') : __('private', 'fluent-boards'),
349 358 // translators: %s is the privacy setting (public or private)
350 359 'message' => sprintf(__('This comment is now %s', 'fluent-boards'), $privacy),
351 360 ], 200);
361 + }
362 +
363 + private function getImageIdsFromRequest(Request $request)
364 + {
365 + $images = $request->getSafe('images');
366 +
367 + if (!$images || !is_array($images)) {
368 + return [];
369 + }
370 +
371 + return array_values(array_filter(array_unique(array_map('intval', $images))));
372 + }
373 +
374 + private function requestHasImagesArray(Request $request)
375 + {
376 + return $request->exists('images') && is_array($request->getSafe('images'));
352 377 }
353 378 }