PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.11.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.11.0
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / app / Http / Controllers / CommentsController.php

CommentsController.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.11.0, at app/Http/Controllers/CommentsController.php

769 lines 28.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Http\Controllers;
4
5 use FluentCommunity\App\Models\Media;
6 use FluentCommunity\App\Models\User;
7 use FluentCommunity\App\Services\CustomSanitizer;
8 use FluentCommunity\App\Services\FeedsHelper;
9 use FluentCommunity\App\Services\Helper;
10 use FluentCommunity\App\Services\ProfileHelper;
11 use FluentCommunity\Framework\Http\Request\Request;
12 use FluentCommunity\App\Models\Comment;
13 use FluentCommunity\App\Models\Feed;
14 use FluentCommunity\App\Models\Reaction;
15 use FluentCommunity\App\Models\XProfile;
16 use FluentCommunity\Framework\Support\Arr;
17
18 class CommentsController extends Controller
19 {
20 public function getComments(Request $request, $feed_id)
21 {
22 $feed = Feed::withoutGlobalScopes()
23 ->byUserAccess(get_current_user_id())
24 ->findOrFail($feed_id);
25
26 if (!in_array($feed->status, FeedsHelper::getViewableByLinkStatuses(), true) && !$feed->hasEditAccess($this->getUserId())) {
27 return $this->sendError([
28 'message' => __('Sorry, you do not have permission to view this post', 'fluent-community')
29 ], 404);
30 }
31
32 /*
33 * The row's own setting is the default the filter gets handed, rather than a bare
34 * true. Before this, meta.enable_comments was read nowhere on this path, so a page
35 * with comments switched off still served its thread to anyone who asked for it.
36 */
37 $canViewComments = apply_filters(
38 'fluent_community/can_view_comments_' . $feed->type,
39 FeedsHelper::commentsEnabled($feed),
40 $feed
41 );
42
43 if (!$canViewComments) {
44 return [
45 'comments' => []
46 ];
47 }
48
49 $comments = Comment::where('post_id', $feed->id)
50 ->byContentModerationAccessStatus($this->getUser())
51 ->orderBy('created_at', 'asc')
52 ->with([
53 'xprofile' => function ($q) {
54 $q->select(ProfileHelper::getXProfilePublicFields());
55 }
56 ])
57 ->whereHas('xprofile', function ($q) {
58 $q->where('status', 'active');
59 })
60 ->get();
61
62 $comments = apply_filters('fluent_community/comments_query_response', $comments, $request->all());
63
64 $userId = $this->getUserId();
65
66 if ($userId) {
67 $likedIds = FeedsHelper::getLikedIdsByUserFeedId($feed->id, get_current_user_id());
68 if ($likedIds) {
69 $comments->each(function ($comment) use ($likedIds) {
70 if (in_array($comment->id, $likedIds)) {
71 $comment->liked = 1;
72 }
73 });
74 }
75 }
76
77 $data = [
78 'comments' => $comments
79 ];
80
81 return apply_filters('fluent_community/comments_api_response', $data, $request->all());
82 }
83
84 public function store(Request $request, $feedId)
85 {
86 $user = $this->getUser(true);
87 do_action('fluent_community/check_rate_limit/create_comment', $user);
88
89 $text = $this->validateCommentText($request->all());
90 $feed = Feed::withoutGlobalScopes()->findOrFail($feedId);
91
92 if (!in_array($feed->status, FeedsHelper::getViewableByLinkStatuses(), true)) {
93 return $this->sendError([
94 'message' => __('This post is not published yet', 'fluent-community')
95 ]);
96 }
97
98 $this->verifyCreateCommentPermission($feed);
99
100 $requestData = $request->all();
101
102 [$markdown, $inlineMedias] = FeedsHelper::replaceImageUrlsWithRealMediaArchive($text);
103 $mentions = FeedsHelper::getMentions($markdown, $feed->space_id, true);
104 $commentHtml = $this->generateCommentHtml($markdown, $mentions);
105
106 $commentData = $this->prepareCommentData($feed->id, $text, $commentHtml);
107
108 if (!empty($requestData['parent_id'])) {
109 $parentId = (int)$requestData['parent_id'];
110 $parentComment = Comment::where('id', $parentId)
111 ->where('post_id', $feed->id)
112 ->first();
113
114 if (!$parentComment) {
115 return $this->sendError([
116 'message' => __('Invalid parent comment', 'fluent-community')
117 ]);
118 }
119
120 $commentData['parent_id'] = $parentId;
121 }
122
123 [$commentData, $mediaItems] = $this->prepareCommentMedia($commentData, $requestData);
124
125 $commentData['is_admin'] = $user->hasSpacePermission('community_moderator', $feed->space);
126
127 if ($mentionUserIds = Arr::get($mentions, 'user_ids', [])) {
128 $commentData['meta']['mentioned_user_ids'] = $mentionUserIds;
129 }
130
131 do_action('fluent_community/before_comment_create', $commentData, $feed);
132
133 $commentData = apply_filters('fluent_community/comment/comment_data', $commentData, $feed);
134
135 // Only comments with text are duplicate checked
136 $shouldCheckDuplicate = $text && !apply_filters('fluent_community/disable_duplicate_comment_check', false, get_current_user_id(), $feed->id);
137
138 // Serialize a member's concurrent submissions by locking their profile row,
139 // so parallel matching requests cannot pass the duplicate check and both insert.
140 $comment = Helper::dbTransaction(function () use ($commentData, $feed, $text, $shouldCheckDuplicate) {
141 XProfile::where('user_id', get_current_user_id())->lockForUpdate()->first();
142
143 if ($shouldCheckDuplicate && Comment::where('user_id', get_current_user_id())->where('message', $text)->where('post_id', $feed->id)->first()) {
144 return null;
145 }
146
147 $newComment = Comment::create($commentData);
148
149 // A held comment is not visible yet, so it must not be counted until it is approved.
150 if ($newComment->status === 'published') {
151 Feed::withoutGlobalScopes()->where('id', $feed->id)->increment('comments_count');
152 }
153
154 return $newComment;
155 });
156
157 if (!$comment) {
158 return $this->sendError([
159 'message' => __('No duplicate comment please!', 'fluent-community')
160 ]);
161 }
162
163 if ($comment->status === 'published') {
164 $feed->comments_count = $feed->comments_count + 1;
165 }
166
167
168 // Merge and save all media in one loop
169 $mediaItems = $mediaItems ? (is_array($mediaItems) ? $mediaItems : [$mediaItems]) : [];
170
171 if ($inlineMedias) {
172 $mediaItems = array_merge($mediaItems, $inlineMedias);
173 }
174
175 if ($mediaItems) {
176 foreach ($mediaItems as $media) {
177 $media->fill([
178 'is_active' => 1,
179 'feed_id' => $feed->id,
180 'object_source' => 'comment',
181 'sub_object_id' => $comment->id
182 ]);
183 $media->save();
184 }
185 }
186
187 $this->loadCommentRelations($comment);
188
189 if ($comment->status != 'published') {
190 do_action('fluent_community/comment/new_comment_' . $comment->status, $comment, $feed);
191 /* translators: %$s is replaced by the status of the comment */
192 $message = sprintf(__('Your comment has been marked as %s', 'fluent-community'), $comment->status);
193 $response = [
194 'comment' => $comment,
195 'message' => $message
196 ];
197 return apply_filters('fluent_community/comment/new_comment_response', $response, $comment);
198 }
199
200 do_action('fluent_community/comment_added_' . $feed->type, $comment, $feed);
201 do_action('fluent_community/comment_added', $comment, $feed, Arr::get($mentions, 'users', []));
202
203 return [
204 'comment' => $comment,
205 'message' => __('Comment has been added', 'fluent-community'),
206 ];
207 }
208
209 public function update(Request $request, $feedId, $commentId)
210 {
211 $text = $this->validateCommentText($request->all());
212
213 $feed = Feed::withoutGlobalScopes()->findOrFail($feedId);
214 $this->verifySpacePermission($feed);
215
216 $requestData = $request->all();
217 $comment = Comment::findOrFail($commentId);
218
219 if ($comment->post_id != $feed->id) {
220 return $this->sendError([
221 'message' => __('Invalid comment', 'fluent-community')
222 ]);
223 }
224
225 $user = $this->getUser(true);
226
227 $requestData['is_admin'] = $user->hasPermissionOrInCurrentSpace('community_moderator', $feed->space);
228
229 if ($comment->user_id != get_current_user_id() && !$user->can('edit_any_comment', $feed->space)) {
230 return $this->sendError([
231 'message' => __('You are not allowed to edit this comment', 'fluent-community')
232 ]);
233 }
234
235 [$markdown, $inlineMedias] = FeedsHelper::replaceImageUrlsWithRealMediaArchive($text, $feed);
236
237 $mentions = FeedsHelper::getMentions($markdown, $feed->space_id);
238
239 $commentHtml = $this->generateCommentHtml($markdown, $mentions);
240
241 $commentData = $this->prepareCommentData($feed->id, $text, $commentHtml);
242
243 [$commentData, $mediaItems] = $this->prepareCommentMedia($commentData, $requestData, $comment);
244
245 $commentData = apply_filters('fluent_community/comment/update_comment_data', $commentData, $feed, $requestData, $comment);
246
247 $comment->fill($commentData);
248
249 $dirty = $comment->getDirty();
250
251 if ($dirty) {
252 $comment->save();
253 }
254
255 // Merge and save all media in one loop
256 $mediaItems = $mediaItems ? (is_array($mediaItems) ? $mediaItems : [$mediaItems]) : [];
257
258 if ($inlineMedias) {
259 $mediaItems = array_merge($mediaItems, $inlineMedias);
260 }
261
262 $allMediaIds = [];
263
264 if ($mediaItems) {
265 foreach ($mediaItems as $media) {
266 $media->fill([
267 'is_active' => 1,
268 'feed_id' => $feed->id,
269 'object_source' => 'comment',
270 'sub_object_id' => $comment->id
271 ]);
272 $media->save();
273 $allMediaIds[] = $media->id;
274 }
275 }
276
277 // Remove old media not in current list
278 $otherMedias = Media::where('object_source', 'comment')
279 ->when($allMediaIds, function ($q) use ($allMediaIds) {
280 $q->whereNotIn('id', $allMediaIds);
281 })
282 ->where('sub_object_id', $comment->id)
283 ->get();
284
285 if (!$otherMedias->isEmpty()) {
286 do_action('fluent_community/comment/media_deleted', $otherMedias);
287 }
288
289 $this->loadCommentRelations($comment);
290
291 if ($dirty) {
292 do_action('fluent_community/comment_updated', $comment, $feed);
293 do_action('fluent_community/comment_updated_' . $feed->type, $comment, $feed);
294 }
295
296 return [
297 'comment' => $comment,
298 'message' => __('Comment has been updated', 'fluent-community'),
299 ];
300 }
301
302 public function patchComment(Request $request, $feedId, $commentId)
303 {
304 $feed = Feed::withoutGlobalScopes()->findOrFail($feedId);
305
306 $comment = Comment::findOrFail($commentId);
307
308 if ($comment->post_id != $feed->id) {
309 return $this->sendError([
310 'message' => __('Invalid comment', 'fluent-community')
311 ]);
312 }
313
314 $user = $this->getUser(true);
315
316 $isMod = $user && $user->hasPermissionOrInCurrentSpace('community_moderator', $feed->space);
317 $isAdmin = $user && $user->hasPermissionOrInCurrentSpace('community_admin', $feed->space);
318
319 if (!$isMod && !$isAdmin) {
320 return $this->sendError([
321 'message' => __('You do not have permission to perform this action', 'fluent-community')
322 ]);
323 }
324
325 $allData = $request->all();
326 $validKeys = ['is_sticky'];
327
328 $data = Arr::only($allData, $validKeys);
329
330 $data = array_map('intval', $data);
331
332 if (isset($data['is_sticky'])) {
333 if ($comment->parent_id) {
334 return $this->sendError([
335 'message' => __('You cannot pin a reply comment', 'fluent-community')
336 ]);
337 }
338
339 $data['is_sticky'] = $data['is_sticky'] ? 1 : 0;
340 if ($data['is_sticky']) {
341 Comment::where('post_id', $feed->id)->update(['is_sticky' => 0]);
342 }
343 }
344
345 if ($data) {
346 $comment->fill($data);
347 $dirty = $comment->getDirty();
348 if ($dirty) {
349 $comment->save();
350 do_action('fluent_community/comment/updated', $comment, $dirty);
351 }
352 }
353
354 return apply_filters('fluent_community/comment/patch_comment_response', [
355 'comment' => $comment,
356 'message' => __('Comment updated', 'fluent-community')
357 ], $comment, $feed, $request->all());
358 }
359
360 private function prepareCommentMedia($commentData, $requestData, $exisitngComment = null)
361 {
362 $mediaImages = Arr::get($requestData, 'media_images', []);
363
364 if ($mediaImages) {
365 if ($exisitngComment) {
366 $mediaItems = [];
367 $mediaData = [];
368 foreach ($mediaImages as $mediaImage) {
369 $id = Arr::get($mediaImage, 'media_id');
370 if ($id) {
371 $media = Media::where('sub_object_id', $exisitngComment->id)
372 ->where('object_source', 'comment')
373 ->find($id);
374 } else {
375 $media = Helper::getMediaFromUrl($mediaImage);
376 }
377
378 if ($media) {
379 $mediaItems[] = $media;
380 $mediaData[] = [
381 'media_id' => $media->id,
382 'url' => $media->public_url,
383 'type' => 'image',
384 'width' => Arr::get($media->settings, 'width'),
385 'height' => Arr::get($media->settings, 'height'),
386 'provider' => Arr::get($media->settings, 'provider', 'uploader')
387 ];
388 }
389 }
390 $commentData['meta']['media_items'] = $mediaData;
391 return [$commentData, $mediaItems];
392 }
393
394 $uploadedImages = Helper::getMediaByProvider($mediaImages);
395 if ($uploadedImages) {
396 $mediaItems = Helper::getMediaItemsFromUrl($uploadedImages);
397 if ($mediaItems) {
398 $mediaPreviews = [];
399 foreach ($mediaItems as $mediaItem) {
400 $mediaData = [
401 'media_id' => $mediaItem->id,
402 'url' => $mediaItem->public_url,
403 'type' => 'image',
404 'width' => Arr::get($mediaItem->settings, 'width'),
405 'height' => Arr::get($mediaItem->settings, 'height'),
406 'provider' => Arr::get($mediaItem->settings, 'provider', 'uploader')
407 ];
408
409 $mediaPreviews[] = array_filter($mediaData);
410 }
411 $commentData['meta']['media_items'] = $mediaPreviews;
412 return [$commentData, $mediaItems];
413 }
414 }
415 }
416
417 if (empty($requestData['meta']['media_preview']['image'])) {
418 return [$commentData, []];
419 }
420
421 if ($exisitngComment) {
422 $image = sanitize_url(Arr::get($requestData, 'meta.media_preview.image', ''));
423 $existingMedia = Media::where('media_url', $image)
424 ->where('object_source', 'comment')
425 ->where('sub_object_id', $exisitngComment->id)
426 ->first();
427
428 if ($existingMedia) {
429 $commentData['meta'] = $exisitngComment->meta;
430 return [$commentData, [$existingMedia]];
431 }
432 }
433
434 // type/provider reach :class bindings and width/height a :style binding in
435 // _MediaPreview.vue. Neither is an executable sink, but the stored values are
436 // request-supplied so they are normalised here rather than trusted.
437 $commentData['meta']['media_preview'] = array_filter([
438 'image' => sanitize_url(Arr::get($requestData, 'meta.media_preview.image', '')),
439 'type' => sanitize_text_field(Arr::get($requestData, 'meta.media_preview.type', 'image')),
440 'provider' => sanitize_text_field(Arr::get($requestData, 'meta.media_preview.provider', '')),
441 'height' => (int) Arr::get($requestData, 'meta.media_preview.height', 0),
442 'width' => (int) Arr::get($requestData, 'meta.media_preview.width', 0),
443 ]);
444
445 return [$commentData, []];
446 }
447
448 private function validateCommentText($data)
449 {
450 $text = trim((string) Arr::get($data, 'comment', ''));
451 $text = CustomSanitizer::unslashMarkdown($text);
452
453 // Decode HTML entities (e.g., &#x20; for space) and strip all whitespace for validation
454 $textForValidation = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
455 $textForValidation = preg_replace('/\s+/u', '', $textForValidation);
456
457 $hasMedia = Arr::get($data, 'media_images', []) || Arr::get($data, 'meta.media_preview.image', false);
458
459 $isReply = !empty($data['parent_id']);
460 if (!$textForValidation && !$hasMedia) {
461 if ($isReply) {
462 throw new \Exception(esc_html__('Reply cannot be empty.', 'fluent-community'), 422);
463 } else {
464 throw new \Exception(esc_html__('Comment cannot be empty.', 'fluent-community'), 422);
465 }
466 }
467
468 $maxCommentLength = apply_filters('fluent_community/max_comment_char_length', 10000);
469 if ($text && strlen($text) > $maxCommentLength) {
470 /* translators: %s is the maximum allowed character count */
471 throw new \Exception(esc_html(sprintf(__('The comment is too long. Please keep it under %s characters.', 'fluent-community'), number_format($maxCommentLength))), 422);
472 }
473
474 return $text;
475 }
476
477 private function verifyCreateCommentPermission($feed)
478 {
479 if (!FeedsHelper::commentsEnabled($feed)) {
480 throw new \Exception(esc_html__('Comments are disabled for this post', 'fluent-community'));
481 }
482
483 $this->verifySpacePermission($feed);
484 }
485
486 private function verifySpacePermission($feed)
487 {
488 if ($feed->space_id && $feed->space) {
489 $user = $this->getUser(true);
490 $user->verifySpacePermission('can_comment', $feed->space);
491
492 if ($feed->space->type == 'course' && Arr::get($feed->space->settings, 'disable_comments') === 'yes') {
493 throw new \Exception(esc_html__('Comments are disabled for this course', 'fluent-community'));
494 }
495 }
496 }
497
498 private function generateCommentHtml($text, $mentions)
499 {
500 $htmlText = $mentions ? $mentions['text'] : $text;
501 return wp_kses_post(FeedsHelper::mdToHtml($htmlText));
502 }
503
504 private function prepareCommentData($feedId, $text, $commentHtml)
505 {
506 return [
507 'post_id' => $feedId,
508 'message' => $text,
509 'message_rendered' => $commentHtml,
510 'type' => 'comment',
511 'meta' => [],
512 ];
513 }
514
515 private function loadCommentRelations($comment)
516 {
517 $comment->load('media');
518 $comment->load([
519 'xprofile' => function ($q) {
520 $q->select(ProfileHelper::getXProfilePublicFields());
521 }
522 ]);
523 }
524
525 public function addOrRemovePostReact(Request $request, $feed_id)
526 {
527 $userId = get_current_user_id();
528 $feed = Feed::withoutGlobalScopes()->byUserAccess($userId)->findOrFail($feed_id);
529 $type = $request->get('react_type', 'like');
530 $type = in_array($type, ['like', 'bookmark'], true) ? $type : 'like';
531 $willRemove = $request->get('remove');
532
533 if (!in_array($feed->status, FeedsHelper::getViewableByLinkStatuses(), true)) {
534 return $this->sendError([
535 'message' => __('This post is not published yet', 'fluent-community')
536 ]);
537 }
538
539 if (!$willRemove && (int) $userId === (int) $feed->user_id && apply_filters('fluent_community/disable_self_post_react', false, $feed)) {
540 return $this->sendError([
541 'message' => __('You cannot react to your own post', 'fluent-community')
542 ]);
543 }
544
545 $react = Reaction::where('user_id', $userId)
546 ->where('object_id', $feed->id)
547 ->where('type', $type)
548 ->objectType('feed')
549 ->first();
550
551 if ($willRemove) {
552 if ($react) {
553 $react->delete();
554 if ($type == 'like') {
555 $feed->reactions_count = $feed->reactions_count - 1;
556 $feed->timestamps = false; // Don't update the updated_at timestamp
557 $feed->save();
558 do_action('fluent_community/feed/react_removed', $feed);
559 }
560 }
561
562 return [
563 'message' => __('Reaction has been removed', 'fluent-community'),
564 'new_count' => $feed->reactions_count
565 ];
566 }
567
568 if ($react) {
569 return [
570 'message' => __('You have already reacted to this post', 'fluent-community'),
571 'new_count' => $feed->reactions_count
572 ];
573 }
574
575 $react = Reaction::create([
576 'user_id' => get_current_user_id(),
577 'object_id' => $feed->id,
578 'type' => $type,
579 'object_type' => 'feed'
580 ]);
581
582 if ($type == 'like') {
583 $feed->reactions_count = $feed->reactions_count + 1;
584 $feed->timestamps = false; // Don't update the updated_at timestamp
585 $feed->save();
586
587 $react->load('xprofile');
588 do_action('fluent_community/feed/react_added', $react, $feed);
589 }
590
591 return [
592 'message' => __('Reaction has been added', 'fluent-community'),
593 'new_count' => $feed->reactions_count
594 ];
595 }
596
597 public function deleteComment(Request $request, $feedId, $commentId)
598 {
599 $feed = Feed::withoutGlobalScopes()->findOrFail($feedId);
600 $comment = Comment::findOrFail($commentId);
601
602 if ($comment->post_id != $feed->id) {
603 return $this->sendError([
604 'message' => __('Invalid comment', 'fluent-community')
605 ]);
606 }
607
608 $user = User::find(get_current_user_id());
609 if ($comment->user_id != get_current_user_id() && !$user->can('delete_any_comment', $feed->space)) {
610 return $this->sendError([
611 'message' => __('You are not allowed to delete this comment', 'fluent-community')
612 ]);
613 }
614
615 do_action('fluent_community/before_comment_delete', $comment);
616
617 if ($comment->media) {
618 do_action('fluent_community/comment/media_deleted', $comment->media);
619 }
620
621 $comment->delete();
622
623 $feed->comments_count = Comment::where('post_id', $feed->id)
624 ->where('status', 'published')
625 ->count();
626 $feed->timestamps = false; // Don't update the updated_at timestamp
627 $feed->save();
628
629 do_action('fluent_community/comment_deleted_' . $feed->type, $commentId, $feed);
630 do_action('fluent_community/comment_deleted', $commentId, $feed);
631
632 return [
633 'message' => __('Selected comment has been deleted', 'fluent-community')
634 ];
635 }
636
637 public function toggleReaction(Request $request, $feedId, $commentId)
638 {
639 $feed = Feed::withoutGlobalScopes()->byUserAccess(get_current_user_id())->findOrFail($feedId);
640 $comment = Comment::findOrFail($commentId);
641
642 if ($comment->post_id != $feed->id) {
643 return $this->sendError([
644 'message' => __('Invalid comment', 'fluent-community')
645 ]);
646 }
647
648 $user = User::findOrFail(get_current_user_id());
649
650 if ($feed->space_id) {
651 $user->verifySpacePermission('registered', $feed->space);
652 }
653
654 $userId = get_current_user_id();
655 $reactionState = !!$request->get('state', false);
656
657 if ($reactionState && (int) $userId === (int) $comment->user_id && apply_filters('fluent_community/disable_self_comment_react', false, $feed)) {
658 return $this->sendError([
659 'message' => __('You cannot react to your own comment', 'fluent-community')
660 ]);
661 }
662
663 if ($reactionState) {
664 // Serialize concurrent reactions on this comment by locking its row,
665 // so parallel add requests cannot each insert a duplicate reaction.
666 $reaction = Helper::dbTransaction(function () use ($comment, $feed) {
667 XProfile::where('user_id', get_current_user_id())->lockForUpdate()->first();
668
669 $reaction = Reaction::firstOrCreate([
670 'user_id' => get_current_user_id(),
671 'object_id' => $comment->id,
672 'object_type' => 'comment',
673 'parent_id' => $feed->id
674 ]);
675
676 if ($reaction->wasRecentlyCreated) {
677 Comment::where('id', $comment->id)->increment('reactions_count');
678 $comment->reactions_count = $comment->reactions_count + 1;
679 }
680
681 return $reaction;
682 });
683
684 if ($reaction->wasRecentlyCreated) {
685 do_action('fluent_community/comment/react_added', $reaction, $comment, $feed);
686 }
687 } else {
688 // remove the reaction
689 $deleted = Reaction::where('user_id', get_current_user_id())
690 ->where('object_id', $comment->id)
691 ->where('object_type', 'comment')
692 ->delete();
693
694 if ($deleted) {
695 $comment->reactions_count = $comment->reactions_count - 1;
696 $comment->save();
697 do_action('fluent_community/comment/react_removed', $comment, $feed);
698 }
699 }
700
701 return [
702 'message' => __('Reaction has been toggled', 'fluent-community'),
703 'reactions_count' => $comment->reactions_count,
704 'liked' => $reactionState
705 ];
706 }
707
708 public function show(Request $request, $id)
709 {
710
711 $testComment = Comment::query()->findOrFail($id);
712
713 $comment = Comment::byContentModerationAccessStatus($this->getUser(), $testComment->space)
714 ->with([
715 'xprofile' => function ($q) {
716 return $q->select(ProfileHelper::getXProfilePublicFields());
717 }
718 ])->findOrFail($id);
719
720 // Just to verify the permission
721 Feed::withoutGlobalScopes()
722 ->byUserAccess($this->getUserId())
723 ->findOrFail($comment->post_id);
724
725 if ($request->get('context') == 'edit') {
726 $meta = $comment->meta;
727 unset($comment->meta);
728 $images = Arr::get($meta, 'media_items', []);
729 if ($images) {
730 $comment->media_images = $images;
731 } else {
732 $preview = Arr::get($meta, 'media_preview', []);
733 if ($preview) {
734 $previewUrl = Arr::get($preview, 'image');
735 $provider = Arr::get($preview, 'provider');
736 if ($previewUrl && $provider == 'uploader') {
737 $media = Media::where('media_url', $previewUrl)
738 ->where('object_source', 'comment')
739 ->where('sub_object_id', $comment->id)
740 ->first();
741 if ($media) {
742 $comment->media_images = [
743 [
744 'media_id' => $media->id,
745 'url' => $media->public_url,
746 'type' => $media->media_type,
747 'width' => Arr::get($media->settings, 'width'),
748 'height' => Arr::get($media->settings, 'height'),
749 'provider' => Arr::get($media->settings, 'provider', 'uploader')
750 ]
751 ];
752 }
753 } else {
754 $comment->meta = [
755 'media_preview' => $preview
756 ];
757 }
758 }
759 }
760 }
761
762 $data = [
763 'comment' => $comment
764 ];
765
766 return apply_filters('fluent_community/comment_api_response', $data, $request->all());
767 }
768 }
769