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

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

354 lines 13.0 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 try {
31 $filter = $request->getSafe('filter', 'sanitize_text_field');
32 $per_page = 10;
33
34 $comments = $this->commentService->getComments($task_id, $per_page, $filter, $board_id);
35 $totalComments = $this->commentService->getTotal($task_id, $board_id);
36
37 return $this->sendSuccess([
38 'comments' => $comments,
39 'total' => $totalComments
40 ], 200);
41 } catch (\Exception $e) {
42 return $this->sendError($e->getMessage(), 404);
43 }
44 }
45
46 /*
47 * handles comment or reply creation
48 * @param $board_id int
49 * @param $task_id int
50 * @return json
51 */
52 public function create(Request $request, $board_id, $task_id)
53 {
54 // TODO: Refactor the whole request and sanitize process here.. minimize the code in this functions.
55 $requestData = [
56 'parent_id' => $request->getSafe('parent_id', function ($value) {
57 return (empty($value)) ? null : intval( $value);
58 }, null),
59 'description' => $request->getSafe('comment', 'sanitize_textarea_field'),
60 'created_by' => $request->getSafe('comment_by', 'intval', get_current_user_id()),
61 'task_id' => (int) $task_id,
62 'type' => $request->getSafe('comment_type', 'sanitize_text_field', 'comment'),
63 'board_id' => (int) $board_id,
64 ];
65 $validationRules = [
66 'description' => 'required|string',
67 'created_by' => 'required|integer',
68 'board_id' => 'required|integer',
69 'task_id' => 'required|integer',
70 'type' => 'required|string'
71 ];
72
73 $images = $request->getSafe('images');
74 if ($images) {
75 $validationRules['description'] = 'nullable|string';
76 }
77
78 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
79
80
81 try {
82
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 }
90 $commentData['settings'] = [ 'raw_description' => $rawDescription, 'mentioned_id' => $mentionData ];
91
92 // Ensure UTF-8 encoding for comment description
93 $description = mb_convert_encoding($commentData['description'], 'UTF-8', 'auto');
94
95 if(!empty($mentionData)) {
96 // Process mentions and links with UTF-8 support
97 $commentData['description'] = $this->commentService->processMentionAndLink($description, $mentionData);
98 } else {
99 // Process links with UTF-8 support
100 $commentData['description'] = $this->commentService->checkIfCommentHaveLinks($description);
101 }
102
103 $comment = $this->commentService->create($commentData, $task_id, $board_id);
104 $comment['user'] = $comment->user;
105
106 $usersToSendEmail = [];
107 if ($comment->type == 'reply') {
108 $parentComment = Comment::findOrFail($comment->parent_id);
109 $commenterId = $parentComment->created_by;
110 if ($commenterId != get_current_user_id())
111 {
112 $commenter = User::select('user_email')->findOrFail($commenterId);
113 $commenterEmail = $commenter->user_email;
114 $usersToSendEmail[] = $commenterEmail;
115 }
116 $this->sendMailAfterComment($comment->id, $usersToSendEmail);
117 } 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);
121 }
122
123 if(!empty($mentionData))
124 {
125 $this->notificationService->mentionInComment($comment, $mentionData);
126 }
127
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 if ($comment->type == 'comment')
142 {
143 $comment->load('replies');
144 }
145
146 return $this->sendSuccess([
147 'message' => __('Comment has been added', 'fluent-boards'),
148 'comment' => $comment
149 ], 201);
150 } catch (\Exception $e) {
151 return $this->sendError($e->getMessage(), 400);
152 }
153 }
154
155 public function update(Request $request, $board_id, $comment_id)
156 {
157 $requestData = [
158 'description' => $request->getSafe('comment', 'sanitize_textarea_field')
159 ];
160
161 $validationRules = [
162 'description' => 'required|string'
163 ];
164
165 $images = $request->getSafe('images');
166 if ($images) {
167 $validationRules['description'] = 'nullable|string';
168 }
169
170 $commentData = $this->commentSanitizeAndValidate($requestData, $validationRules);
171
172 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));
178 }
179
180 $comment = $this->commentService->update($commentData, $comment_id, $mentionData, $board_id);
181
182 if (!$comment) {
183 $errorMessage = __('Unauthorized Action', 'fluent-boards');
184 return $this->sendError($errorMessage, 401);
185 }
186
187 if(!empty($mentionData))
188 {
189 $this->notificationService->mentionInComment($comment, $mentionData);
190 }
191
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 }
200 }
201
202 $comment->load('user');
203
204 return $this->sendSuccess([
205 'comment' => $comment,
206 'message' => __('Comment has been updated', 'fluent-boards'),
207 ], 200);
208 } catch (\Exception $e) {
209 return $this->sendError($e->getMessage(), 404);
210 }
211 }
212
213 public function deleteComment($board_id, $comment_id)
214 {
215 try {
216 $this->commentService->delete($comment_id, $board_id);
217
218 return $this->sendSuccess([
219 'message' => __('Comment has been deleted', 'fluent-boards'),
220 ], 200);
221 } catch (\Exception $e) {
222 return $this->sendError($e->getMessage(), 404);
223 }
224 }
225
226 public function updateReply(Request $request, $board_id, $reply_id)
227 {
228 $requestData = [
229 'description' => $request->getSafe('comment', 'sanitize_textarea_field')
230 ];
231
232 $validationRules = [
233 'description' => 'required|string'
234 ];
235
236 $replyData = $this->commentSanitizeAndValidate($requestData, $validationRules);
237
238 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 }
245
246 $reply = $this->commentService->update($replyData, $reply_id, $mentionData, $board_id);
247
248 if (!$reply) {
249 $errorMessage = __('Unauthorized Action', 'fluent-boards');
250 return $this->sendError($errorMessage, 401);
251 }
252
253 return $this->sendSuccess([
254 'description' => $reply->description,
255 'message' => __('Reply has been updated', 'fluent-boards'),
256 ], 200);
257 } catch (\Exception $e) {
258 return $this->sendError($e->getMessage(), 404);
259 }
260 }
261
262 public function deleteReply($board_id, $reply_id)
263 {
264 try {
265 $this->commentService->deleteReply($reply_id, $board_id);
266
267 return $this->sendSuccess([
268 'message' => __('Reply has been deleted', 'fluent-boards'),
269 ], 200);
270 } catch (\Exception $e) {
271 return $this->sendError($e->getMessage(), 404);
272 }
273 }
274
275 public function sendMailAfterComment($commentId, $usersToSendEmail)
276 {
277 $current_user_id = get_current_user_id();
278
279 /* this will run in background as soon as possible */
280 /* 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');
282 }
283
284 private function commentSanitizeAndValidate($data, array $rules = [])
285 {
286 $data = Helper::sanitizeComment($data);
287
288 return $this->validate($data, $rules);
289 }
290
291 public function handleImageUpload(Request $request, $board_id, $task_id)
292 {
293 $allowedTypes = implode(',', [
294 "image/jpeg",
295 "image/gif",
296 "image/png",
297 "image/bmp",
298 "image/tiff",
299 "image/webp",
300 "image/avif",
301 "image/x-icon",
302 "image/heic",
303 ]);
304
305 $files = $this->validate($request->files(), [
306 'file' => 'mimetypes:' . $allowedTypes,
307 ], [
308 'file.mimetypes' => __('The file must be a image type.', 'fluent-boards')
309 ]);
310
311 (new \FluentBoards\App\Services\TaskService())->findTaskOnBoard($task_id, $board_id);
312
313 $uploadInfo = UploadService::handleFileUpload( $files, $board_id);
314
315 $imageData = $uploadInfo[0];
316 $attachment = $this->commentService->createCommentImage($imageData, $board_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);
325
326 return $this->sendSuccess([
327 'message' => __('attachment has been added', 'fluent-boards'),
328 'imageAttachment' => $attachment
329 ], 200);
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