PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.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 1.40 1.41 All 42 releases
← All changes | app/Http/Controllers/CommentController.php +161 -55 1.222.1.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,32 +75,48 @@
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 121 return $this->sendSuccess([
105 122 'message' => __('Comment has been added', 'fluent-boards'),
@@ -105,19 +122,16 @@
105 122 'message' => __('Comment has been added', 'fluent-boards'),
106 123 'comment' => $comment
107 124 ], 201);
108 125 } catch (\Exception $e) {
109 - return $this->sendError($e->getMessage(), 404);
126 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 400);
110 127 }
111 128 }
112 129
113 130 public function update(Request $request, $board_id, $comment_id)
114 131 {
115 -// $commentData = $this->commentSanitizeAndValidate($request->all(), [
116 -// 'description' => 'required|string',
117 -// ]);
118 132 $requestData = [
119 - 'description' => $request->comment
133 + 'description' => $this->commentService->sanitizeContent($request->get('comment', ''))
120 134 ];
121 135
122 136 $validationRules = [
123 137 'description' => 'required|string'
@@ -122,9 +136,11 @@
122 136 $validationRules = [
123 137 'description' => 'required|string'
124 138 ];
125 139
126 - if ($request->images) {
140 + $hasImagesParam = $this->requestHasImagesArray($request);
141 + $imageIds = $this->getImageIdsFromRequest($request);
142 + if ($hasImagesParam) {
127 143 $validationRules['description'] = 'nullable|string';
128 144 }
129 145
130 146 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
@@ -129,39 +145,51 @@
129 145
130 146 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
131 147
132 148 try {
133 - $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 + }
134 157
135 - if($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);
165 + }
166 +
167 + if(!empty($mentionData))
136 168 {
137 - $this->notificationService->mentionInComment($comment, $request->mentionData);
169 + $this->notificationService->mentionInComment($comment, $mentionData);
138 170 }
139 171
140 - if($request->images)
141 - {
142 - $this->commentService->attachCommentImages($comment, $request->images);
172 + if ($hasImagesParam) {
173 + $this->commentService->attachCommentImages($comment, $imageIds);
143 174 $comment->load(['images']);
144 175 }
145 176
146 - if ( !$comment ) {
147 - $errorMessage = __('Unauthorized Action', 'fluent-boards');
148 - return $this->sendError($errorMessage, 401);
149 - }
177 + $comment->load('user');
150 178
151 179 return $this->sendSuccess([
152 - 'description' => $comment->description,
180 + 'comment' => $comment,
153 181 'message' => __('Comment has been updated', 'fluent-boards'),
154 182 ], 200);
155 183 } catch (\Exception $e) {
156 - return $this->sendError($e->getMessage(), 404);
184 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 404);
157 185 }
158 186 }
159 187
160 - public function delete($board_id, $comment_id)
188 + public function deleteComment($board_id, $comment_id)
161 189 {
162 190 try {
163 - $this->commentService->delete($comment_id);
191 + $this->commentService->delete($comment_id, $board_id);
164 192
165 193 return $this->sendSuccess([
166 194 'message' => __('Comment has been deleted', 'fluent-boards'),
167 195 ], 200);
@@ -172,9 +200,9 @@
172 200
173 201 public function updateReply(Request $request, $board_id, $reply_id)
174 202 {
175 203 $requestData = [
176 - 'description' => $request->comment
204 + 'description' => $this->commentService->sanitizeContent($request->get('comment', ''))
177 205 ];
178 206
179 207 $validationRules = [
180 208 'description' => 'required|string'
@@ -182,9 +210,11 @@
182 210
183 211 $replyData = $this->commentSanitizeAndValidate($requestData, $validationRules);
184 212
185 213 try {
186 - $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);
187 217
188 218 if (!$reply) {
189 219 $errorMessage = __('Unauthorized Action', 'fluent-boards');
190 220 return $this->sendError($errorMessage, 401);
@@ -194,9 +224,9 @@
194 224 'description' => $reply->description,
195 225 'message' => __('Reply has been updated', 'fluent-boards'),
196 226 ], 200);
197 227 } catch (\Exception $e) {
198 - return $this->sendError($e->getMessage(), 404);
228 + return $this->sendError($e->getMessage(), $e->getCode() === 403 ? 403 : 404);
199 229 }
200 230 }
201 231
202 232 public function deleteReply($board_id, $reply_id)
@@ -201,9 +231,9 @@
201 231
202 232 public function deleteReply($board_id, $reply_id)
203 233 {
204 234 try {
205 - $this->commentService->deleteReply($reply_id);
235 + $this->commentService->deleteReply($reply_id, $board_id);
206 236
207 237 return $this->sendSuccess([
208 238 'message' => __('Reply has been deleted', 'fluent-boards'),
209 239 ], 200);
@@ -211,17 +241,47 @@
211 241 return $this->sendError($e->getMessage(), 404);
212 242 }
213 243 }
214 244
215 - public function sendMailAfterComment($commentId, $usersToSendEmail)
245 + public function sendMailAfterComment($commentId, $recipientUserIds)
216 246 {
217 247 $current_user_id = get_current_user_id();
218 248
219 249 /* this will run in background as soon as possible */
220 250 /* sending Model or Model Instance won't work here */
221 - as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $usersToSendEmail, $current_user_id], 'fluent-boards');
251 + as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_comment', [$commentId, $recipientUserIds, $current_user_id], 'fluent-boards');
222 252 }
223 253
254 + /**
255 + * Sanitize mention IDs and optionally verify board membership before a create.
256 + *
257 + * @param Request $request
258 + * @param int $boardId
259 + * @return array
260 + * @throws \Exception
261 + */
262 + private function getMentionData(Request $request, $boardId = null)
263 + {
264 + $rawMentionData = $request->getSafe('mentionData');
265 + if (!is_array($rawMentionData)) {
266 + return [];
267 + }
268 +
269 + $mentionData = array_values(array_unique(array_filter(array_map('absint', $rawMentionData))));
270 +
271 + if (!$boardId) {
272 + return $mentionData;
273 + }
274 +
275 + $boardMemberIds = $this->notificationService->resolveBoardMentionUserIds($boardId, $mentionData);
276 +
277 + if (array_diff($mentionData, $boardMemberIds)) {
278 + throw new \Exception(esc_html__('One or more mentioned users are not members of this board', 'fluent-boards'), 403);
279 + }
280 +
281 + return $boardMemberIds;
282 + }
283 +
224 284 private function commentSanitizeAndValidate($data, array $rules = [])
225 285 {
226 286 $data = Helper::sanitizeComment($data);
227 287
@@ -247,13 +307,22 @@
247 307 ], [
248 308 'file.mimetypes' => __('The file must be a image type.', 'fluent-boards')
249 309 ]);
250 310
311 + (new \FluentBoards\App\Services\TaskService())->findTaskOnBoard($task_id, $board_id);
312 +
251 313 $uploadInfo = UploadService::handleFileUpload( $files, $board_id);
252 314
253 315 $imageData = $uploadInfo[0];
254 -// $attachmentService = new AttachmentService();
255 - $attachment = $this->commentService->createCommentImage($imageData, $board_id);
316 + $attachment = $this->commentService->createCommentImage($imageData, $board_id, $task_id);
317 + if(!!defined('FLUENT_BOARDS_PRO_VERSION')) {
318 + $mediaData = (new AttachmentService())->processMediaData($imageData, $files['file']);
319 + $attachment['driver'] = $mediaData['driver'];
320 + $attachment['file_path'] = $mediaData['file_path'];
321 + $attachment['full_url'] = $mediaData['full_url'];
322 + $attachment->save();
323 + }
324 + $attachment->public_url = $this->commentService->createPublicUrl($attachment, $board_id);
256 325
257 326 return $this->sendSuccess([
258 327 'message' => __('attachment has been added', 'fluent-boards'),
259 328 'imageAttachment' => $attachment
@@ -258,6 +327,43 @@
258 327 'message' => __('attachment has been added', 'fluent-boards'),
259 328 'imageAttachment' => $attachment
260 329 ], 200);
261 330
331 + }
332 +
333 + public function updateCommentPrivacy($board_id, $comment_id)
334 + {
335 + $comment = $this->commentService->findCommentOnBoard($comment_id, $board_id);
336 +
337 + // Check if user has permission to update the comment
338 + if ($comment->created_by != get_current_user_id()) {
339 + return $this->sendError(__('Unauthorized Action', 'fluent-boards'), 401);
340 + }
341 +
342 + // Toggle privacy
343 + $comment->privacy = ($comment->privacy === 'public') ? 'private' : 'public';
344 + $comment->save();
345 +
346 + return $this->sendSuccess([
347 + 'comment' => $comment,
348 + $privacy = $comment->privacy == 'public' ? __('public', 'fluent-boards') : __('private', 'fluent-boards'),
349 + // translators: %s is the privacy setting (public or private)
350 + 'message' => sprintf(__('This comment is now %s', 'fluent-boards'), $privacy),
351 + ], 200);
352 + }
353 +
354 + private function getImageIdsFromRequest(Request $request)
355 + {
356 + $images = $request->getSafe('images');
357 +
358 + if (!$images || !is_array($images)) {
359 + return [];
360 + }
361 +
362 + return array_values(array_filter(array_unique(array_map('intval', $images))));
363 + }
364 +
365 + private function requestHasImagesArray(Request $request)
366 + {
367 + return $request->exists('images') && is_array($request->getSafe('images'));
262 368 }
263 369 }