PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.95
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.95
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 1.1.0 All 77 releases
fluent-community / app / Http / Controllers / FeedsController.php

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

1,073 lines 35.6 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\Functions\Utility;
6 use FluentCommunity\App\Models\Media;
7 use FluentCommunity\App\Models\NotificationSubscriber;
8 use FluentCommunity\App\Models\Space;
9 use FluentCommunity\App\Models\User;
10 use FluentCommunity\App\Services\CustomSanitizer;
11 use FluentCommunity\App\Services\FeedsHelper;
12 use FluentCommunity\App\Services\Helper;
13 use FluentCommunity\App\Services\Libs\FileSystem;
14 use FluentCommunity\App\Services\ProfileHelper;
15 use FluentCommunity\App\Services\RemoteUrlParser;
16 use FluentCommunity\Framework\Http\Request\Request;
17 use FluentCommunity\App\Models\Comment;
18 use FluentCommunity\App\Models\Feed;
19 use FluentCommunity\App\Models\Reaction;
20 use FluentCommunity\App\Models\BaseSpace;
21 use FluentCommunity\Framework\Support\Arr;
22
23 class FeedsController extends Controller
24 {
25 public function get(Request $request)
26 {
27 $bySpace = $request->get('space');
28 $userId = $request->getSafe('user_id', 'intval', '');
29 $selectedTopic = $request->getSafe('topic_slug', 'sanitize_text_field', '');
30 $search = $request->get('search');
31 if ($bySpace) {
32 // just for validation
33 $space = BaseSpace::where('slug', $bySpace)->first();
34 if (!$space) {
35 return $this->sendError('Invalid space slug');
36 }
37 }
38
39 $feedsQuery = Feed::where('status', 'published')
40 ->select(Feed::$publicColumns)
41 ->with([
42 'xprofile' => function ($q) {
43 $q->select(ProfileHelper::getXProfilePublicFields());
44 },
45 'comments.xprofile' => function ($q) {
46 $q->select(ProfileHelper::getXProfilePublicFields());
47 },
48 'space',
49 'reactions' => function ($q) {
50 $q->with([
51 'xprofile' => function ($query) {
52 $query->select(['user_id', 'avatar']);
53 }
54 ])
55 ->where('type', 'like')
56 ->limit(3);
57 },
58 'terms' => function ($q) {
59 $q->select(['title', 'slug'])
60 ->where('taxonomy_name', 'post_topic');
61 }
62 ]
63 )
64 ->searchBy($search)
65 ->byTopicSlug($selectedTopic)
66 ->customOrderBy($request->get('type', ''));
67
68 $stickyFeed = null;
69
70 $disableSticky = $request->get('disable_sticky', '') == 'yes' || !!$search || !!$selectedTopic;
71
72 if ($bySpace && !$disableSticky) {
73 $feedsQuery = $feedsQuery->filterBySpaceSlug($bySpace)
74 ->where('is_sticky', 0);
75 if ($request->page == 1) {
76 $stickyFeed = Feed::where('space_id', $space->id)
77 ->where('is_sticky', 1)
78 ->with([
79 'xprofile' => function ($q) {
80 $q->select(ProfileHelper::getXProfilePublicFields());
81 },
82 'comments.xprofile' => function ($q) {
83 $q->select(ProfileHelper::getXProfilePublicFields());
84 },
85 'space'
86 ]
87 )
88 ->first();
89 }
90 }
91
92 if ($userId) {
93 $feedsQuery = $feedsQuery->where('user_id', $userId);
94 if ($userId != get_current_user_id()) {
95 $feedsQuery = $feedsQuery->byUserAccess(get_current_user_id());
96 }
97 } else {
98 $feedsQuery->byUserAccess(get_current_user_id());
99 }
100
101 do_action_ref_array('fluent_community/feeds_query', [&$feedsQuery, $request->all()]);
102
103 $feeds = $feedsQuery->paginate();
104
105 // add $stickyFeed to the first page
106 if ($stickyFeed) {
107 $stickyFeed = $this->transformFeed($stickyFeed);
108 }
109
110 $feeds->getCollection()->each(function ($feed) {
111 $this->transformFeed($feed);
112 });
113
114 $data = [
115 'feeds' => $feeds,
116 'sticky' => $stickyFeed
117 ];
118
119 $isMainFeed = $request->get('page') == 1 && !$search && !$userId;
120 if ($isMainFeed && get_current_user_id()) {
121 $data['last_fetched_timestamp'] = current_time('timestamp');
122 }
123
124 return $data;
125 }
126
127 public function getFeedBySlug(Request $request, $feed_slug)
128 {
129 if ($request->get('context') == 'edit') {
130 $feed = Feed::where('slug', $feed_slug)->with(['space'])->first();
131
132 if (!$feed || !$feed->hasEditAccess(get_current_user_id())) {
133 return $this->sendError([
134 'message' => 'You do not have permission to edit this feed'
135 ]);
136 }
137
138 return [
139 'feed' => $feed
140 ];
141 }
142
143 $feed = Feed::where('slug', $feed_slug)
144 ->select(Feed::$publicColumns)
145 ->with([
146 'xprofile' => function ($q) {
147 $q->select(ProfileHelper::getXProfilePublicFields());
148 },
149 'space',
150 'comments.xprofile' => function ($q) {
151 $q->select(ProfileHelper::getXProfilePublicFields());
152 },
153 'reactions' => function ($q) {
154 $q->with([
155 'xprofile' => function ($query) {
156 $query->select(['user_id', 'avatar']);
157 }
158 ])
159 ->where('type', 'like')
160 ->limit(3);
161 },
162 'terms' => function ($q) {
163 $q->select(['title', 'slug'])
164 ->where('taxonomy_name', 'post_topic');
165 }
166 ])
167 ->byUserAccess($this->getUserId())
168 ->first();
169
170 if (!$feed) {
171 return $this->sendError([
172 'message' => __('The feed could not be found', 'fluent-commuity')
173 ], 404);
174 }
175
176 $this->transformFeed($feed);
177
178 return [
179 'feed' => $feed
180 ];
181 }
182
183 public function getBookmarks(Request $request)
184 {
185 $userId = get_current_user_id();
186
187 $feedsQuery = Feed::where('status', 'published')
188 ->select(Feed::$publicColumns)
189 ->with([
190 'xprofile' => function ($q) {
191 $q->select(ProfileHelper::getXProfilePublicFields());
192 },
193 'comments.xprofile' => function ($q) {
194 $q->select(ProfileHelper::getXProfilePublicFields());
195 },
196 'space'
197 ]
198 )
199 ->byBookMarked($userId)
200 ->byUserAccess($userId)
201 ->searchBy($request->get('search'));
202
203
204 if ($type = $request->get('type')) {
205 $feedsQuery = $feedsQuery->where('type', $type);
206 }
207
208 $feeds = $feedsQuery->orderBy('id', 'DESC')
209 ->paginate();
210
211 $feeds->getCollection()->each(function ($feed) {
212 $this->transformFeed($feed);
213 });
214
215 $data = [
216 'feeds' => $feeds
217 ];
218
219 if ($request->get('page') == 1) {
220 $lastItem = FeedsHelper::getLastFeedId();
221 if ($lastItem) {
222 $data['last_id'] = $lastItem;
223 }
224 }
225
226 return $data;
227 }
228
229 public function store(Request $request)
230 {
231 $userId = get_current_user_id();
232 $user = $this->getUser(true);
233
234 do_action('fluent_community/check_rate_limit/create_post', $user);
235
236 $requestData = $request->all();
237
238 $data = $this->sanitizeAndValidateData($requestData);
239
240 if ($isDulicate = $this->checkForDuplicatePost($userId, $data['message'])) {
241 return $isDulicate;
242 }
243
244 $feed = new Feed();
245 $feed->user_id = $userId;
246
247 if ($spaceSlug = $request->get('space')) {
248 $data['space_id'] = $this->validateAndSetSpace($spaceSlug, $user);
249 } else {
250 // Check if the user has global post permission
251 if (!Helper::hasGlobalPost()) {
252 return $this->sendError([
253 'message' => __('Please select a valid space to post in', 'fluent-community')
254 ]);
255 }
256 }
257
258 $message = $data['message'];
259
260 $mentions = FeedsHelper::getMentions($data['message'], Arr::get($data, 'space_id'));
261
262 if ($mentions) {
263 $data['message'] = $message;
264 $message = $mentions['text'];
265 }
266
267 // replace new line with br
268 $data['message_rendered'] = wp_kses_post(FeedsHelper::mdToHtml($message));
269
270 $mediaItems = null;
271
272 if (!empty($data['survey'])) {
273 $this->handleSurveyConfig($data);
274 } elseif (Arr::get($requestData, 'meta.media_preview.provider') == 'giphy') {
275 $this->setGiphyMediaPreview($data, $requestData);
276 } else {
277 $mediaItems = $this->processNewMedia($requestData, $data);
278 }
279
280 $data = apply_filters('fluent_community/feed/new_feed_data', $data, $request->all());
281
282 $feed->fill($data);
283
284 $feed->save();
285
286 if ($mediaItems) {
287 $this->saveMediaItems($feed, $mediaItems);
288 }
289
290 $this->handleMentions($feed, $mentions ?? []);
291
292 $feed->load(['xprofile', 'comments.xprofile']);
293
294 if ($feed->space_id) {
295 $feed->load(['space']);
296 $topicIds = (array)$request->get('topic_ids', []);
297 // take only max topics per post
298 if ($topicIds) {
299 $topicsConfig = Helper::getTopicsConfig();
300 $topicIds = array_slice($topicIds, 0, $topicsConfig['max_topics_per_post']);
301 $feed->attachTopics($topicIds, false);
302 }
303 }
304
305 do_action('fluent_community/feed/created', $feed);
306
307 if ($feed->space_id) {
308 do_action('fluent_community/space_feed/created', $feed);
309 }
310
311 return [
312 'feed' => $feed,
313 'message' => __('Feed added', 'fluent-community'),
314 'last_fetched_timestamp' => current_time('timestamp')
315 ];
316 }
317
318 public function update(Request $request, $feedId)
319 {
320 $requestData = $request->all();
321 $data = $this->sanitizeAndValidateData($requestData);
322
323 $userId = get_current_user_id();
324 $user = User::findOrFail($userId);
325
326 $feed = Feed::find($feedId);
327
328 if (!$feed) {
329 return $this->sendError(['message' => __('Feed not found', 'fluent-community')]);
330 }
331
332 $user->canEditFeed($feed, false);
333
334 if (!$feed->hasEditAccess($userId)) {
335 return $this->send('You do not have permission to edit this feed', 403);
336 }
337
338 if ($spaceSlug = $request->get('space')) {
339 $data['space_id'] = $this->validateAndSetSpace($spaceSlug, $user);
340 }
341
342 $message = $data['message'];
343
344 $mentions = FeedsHelper::getMentions($data['message'], Arr::get($data, 'space_id'));
345
346 if ($mentions) {
347 $data['message'] = $message;
348 $message = $mentions['text'];
349 }
350
351 // replace new line with br
352 $data['message_rendered'] = wp_kses_post(FeedsHelper::mdToHtml($message));
353 $mediaItems = null;
354
355 if (!empty($data['survey'])) {
356 $this->handleSurveyConfig($data);
357 } elseif (Arr::get($requestData, 'meta.media_preview.provider') == 'giphy') {
358 $this->setGiphyMediaPreview($data, $requestData);
359 } else {
360 $mediaItems = $this->processExistingMedia($feed, $requestData, $data);
361 }
362
363 if ($message != $feed->message) {
364 $meta = $feed->meta;
365 $meta['last_edited'] = [
366 'user_id' => $userId,
367 'time' => current_time('mysql')
368 ];
369
370 $editHistory = $feed->getCustomMeta('_edit_history', []);
371
372 if (!$editHistory) {
373 $editHistory = [];
374 }
375
376 $editHistory[] = array_filter([
377 'user_id' => $userId,
378 'time' => current_time('mysql'),
379 'prev_message' => $feed->message,
380 'prev_title' => $feed->title
381 ]);
382
383 // get last 5 edit history
384 $editHistory = array_slice($editHistory, -5);
385 $feed->updateCustomMeta('_edit_history', $editHistory);
386 $data['meta'] = $meta;
387 }
388
389 $data = apply_filters('fluent_community/feed/update_data', $data, $feed);
390 $feed->fill($data);
391 $dirty = $feed->getDirty();
392
393 if ($dirty) {
394 $feed->save();
395 }
396
397 if ($mediaItems) {
398 $this->saveMediaItems($feed, $mediaItems);
399 }
400
401
402 $feed->load(['xprofile', 'comments.xprofile']);
403
404 if ($feed->space_id) {
405 $feed->load(['space']);
406 }
407
408 if ($dirty) {
409 do_action('fluent_community/feed/updated', $feed, $dirty);
410 }
411
412 return [
413 'feed' => $feed,
414 'message' => __('Feed updated', 'fluent-community')
415 ];
416 }
417
418 public function patchFeed(Request $request, $feedId)
419 {
420 $feed = Feed::findOrFail($feedId);
421 $user = $this->getUser(true);
422
423 $isMod = $user->isCommunityModerator();
424 $isAuthor = $feed->user_id == $user->ID;
425
426 if (!$isMod && !$isAuthor) {
427 return $this->sendError([
428 'message' => __('You do not have permission to perform this action', 'fluent-community')
429 ]);
430 }
431
432 $allData = $request->all();
433 $validKeys = ['is_sticky', 'priority', 'comments_disabled'];
434
435 if (!$isMod) {
436 $validKeys = ['comments_disabled'];
437 }
438
439 $data = Arr::only($allData, $validKeys);
440
441 $data = array_map('intval', $data);
442
443 if (isset($data['is_sticky'])) {
444 $data['is_sticky'] = $data['is_sticky'] ? 1 : 0;
445 if ($data['is_sticky'] && $feed->space_id) {
446 // remove all the sticky posts from the space
447 Feed::where('space_id', $feed->space_id)->update(['is_sticky' => 0]);
448 }
449 }
450
451 if (isset($data['comments_disabled'])) {
452 $meta = $feed->meta;
453 $meta['comments_disabled'] = $data['comments_disabled'] ? 'yes' : 'no';
454 $data['meta'] = $meta;
455 }
456
457
458 if ($data) {
459 $feed->fill($data);
460 $dirty = $feed->getDirty();
461 if ($dirty) {
462 $feed->save();
463 do_action('fluent_community/feed/updated', $feed, $dirty);
464 }
465 }
466
467 return [
468 'feed' => $feed,
469 'message' => __('Feed updated', 'fluent-community')
470 ];
471 }
472
473 public function getLinks(Request $request)
474 {
475 return [
476 'links' => Helper::getFeedLinks()
477 ];
478 }
479
480 public function updateLinks(Request $request)
481 {
482 $links = $request->get('links', []);
483
484 $links = array_map(function ($link) {
485 return CustomSanitizer::santizeLinkItem($link);
486 }, $links);
487
488 Helper::updateFeedLinks($links);
489
490 return [
491 'message' => __('Links has been updated', 'fluent-community'),
492 'links' => $links
493 ];
494 }
495
496 private function setGiphyMediaPreview(&$data, $requestData)
497 {
498 if (empty(Arr::get($requestData, 'meta.media_preview.image'))) {
499 return;
500 }
501
502 $data['meta']['media_preview'] = array_filter([
503 'image' => sanitize_url($requestData['meta']['media_preview']['image']),
504 'type' => Arr::get($requestData, 'meta.media_preview.type', 'image'),
505 'provider' => Arr::get($requestData, 'meta.media_preview.provider', ''),
506 'height' => Arr::get($requestData, 'meta.media_preview.height', 0),
507 'width' => Arr::get($requestData, 'meta.media_preview.width', 0),
508 ]);
509 }
510
511 private function handleSurveyConfig(&$data)
512 {
513 if (empty($data['meta'])) {
514 $data['meta'] = [];
515 }
516
517 $data['meta']['survey_config'] = $data['survey'];
518 $data['content_type'] = 'survey';
519 }
520
521 private function processNewMedia($requestData, &$data)
522 {
523 if ($mediaImages = Arr::get($requestData, 'media_images')) {
524 $uploadedImages = Helper::getMediaByProvider($mediaImages);
525 $uploadedMediaItems = Helper::getMediaItemsFromUrl($uploadedImages);
526 $mediaPreviews = $this->generateMediaPreviews($uploadedMediaItems);
527 $this->formatMediaMeta($mediaPreviews, $data, $mediaImages);
528 return $uploadedMediaItems;
529 }
530
531 if ($media = Arr::get($requestData, 'media')) {
532 $type = Arr::get($media, 'type', 'oembed');
533 if ($type == 'oembed') {
534 $url = Arr::get($media, 'url');
535 $metaData = RemoteUrlParser::parse($url);
536 if ($metaData && !is_wp_error($metaData) && (!empty($metaData['image']) || !empty($metaData['html']))) {
537 $data['meta']['media_preview'] = $metaData;
538 return [];
539 }
540 }
541 }
542
543 $urlMeta = $this->parseFirstUrl($data['message_rendered']);
544
545 if ($urlMeta) {
546 $data['meta'] = $urlMeta;
547 return [];
548 }
549
550 // Let's give option to the user to check if there is any fallback
551 do_action_ref_array('fluent_community/feed/meta_fallback', [&$data]);
552
553 return [];
554 }
555
556 private function processExistingMedia($feed, $requestData, &$data)
557 {
558 $images = (array)Arr::get($requestData, 'media_images', []);
559 $mediaImages = Helper::getMediaByProvider($images);
560 $metaMediaMetaItems = Helper::getMediaByProvider((array)Arr::get($requestData, 'meta.media_items', []));
561 $metaMediaPreview = array_filter((array)Arr::get($requestData, 'meta.media_preview', []));
562 $requestMediaIds = array_column($metaMediaMetaItems, 'media_id');
563
564 if (count($mediaImages) == 0 && count($metaMediaMetaItems) == 0) {
565 if (count($metaMediaPreview) === 0) {
566 do_action('fluent_community/feed/media_deleted', $feed->media);
567 $data['meta']['media_preview'] = null;
568 }
569
570 $previewMeta = $this->parseFirstUrl($data['message_rendered']);
571 if (count($metaMediaPreview) > 0) {
572 $data['meta']['media_preview'] = $metaMediaPreview;
573 } elseif (count($previewMeta) > 0) {
574 $data['meta'] = $previewMeta;
575 }
576
577 return [];
578 }
579
580 if (count($mediaImages) == 1 && (count($metaMediaMetaItems) == 0 || count($metaMediaPreview) > 0)) {
581 if (Arr::get($metaMediaPreview, 'is_uploaded')) {
582 $mediaImages[] = $metaMediaPreview['image'] . '?media_key=' . $feed->media[0]->media_key;
583 unset($data['meta']['media_preview']);
584 } elseif (count($metaMediaPreview) > 0) {
585 do_action('fluent_community/feed/media_deleted', $feed->media);
586 }
587
588 $mediaItems = Helper::getMediaItemsFromUrl($mediaImages);
589 $mediaPreviews = $this->generateMediaPreviews($mediaItems);
590
591 $this->formatMediaMeta($mediaPreviews, $data, $images);
592
593 return $mediaItems;
594 }
595
596 $message = $data['message'];
597
598 if ($mentions = FeedsHelper::getMentions($message, Arr::get($data, 'space_id'))) {
599 $message = $mentions['text'];
600 }
601
602 $data['message_rendered'] = FeedsHelper::mdToHtml($message);
603
604 if (count($mediaImages) > 1) {
605 $mediaItems = $this->processNewMedia($requestData, $data);
606 }
607
608 $deletedMediaItems = $feed->media()->whereNotIn('id', $requestMediaIds)->get();
609 do_action('fluent_community/feed/media_deleted', $deletedMediaItems);
610
611
612 if (!isset($data['meta']['media_items'])) {
613 $data['meta']['media_items'] = [];
614 }
615
616 if (!isset($data['meta']['media_preview'])) {
617 $data['meta']['media_preview'] = null;
618 }
619
620 if ($metaMediaMetaItems) {
621 $filteredData = array_filter($metaMediaMetaItems, function ($item) use ($requestMediaIds) {
622 return in_array($item['media_id'], $requestMediaIds);
623 });
624
625 if (count($mediaImages) == 1 && count($filteredData) > 0) {
626 $mediaItems = Helper::getMediaItemsFromUrl($mediaImages);
627 $newMediaItems = $this->generateMediaPreviews($mediaItems);
628 $filteredData = array_merge($filteredData, $newMediaItems);
629 }
630
631 $data['meta']['media_items'] = array_merge($filteredData, $data['meta']['media_items']);
632 }
633
634 if (isset($feed->meta['media_preview'])) {
635 if (count($mediaImages) > 1) {
636 $data['meta']['media_preview'] = null;
637 }
638 }
639
640 return $mediaItems ?? [];
641 }
642
643 private function generateMediaPreviews($mediaItems)
644 {
645 $mediaPreviews = [];
646 foreach ($mediaItems as $media) {
647 if (!$media || !$media->is_active) {
648 $this->sendError(['message' => 'Invalid media image. Please upload a new one.']);
649 }
650
651 $data = [
652 'media_id' => $media->id,
653 'url' => $media->public_url,
654 'type' => 'image',
655 'width' => Arr::get($media->settings, 'width'),
656 'height' => Arr::get($media->settings, 'height'),
657 'provider' => Arr::get($media->settings, 'provider', 'uploader')
658 ];
659
660 $mediaPreviews[] = array_filter($data);
661 }
662
663 return $mediaPreviews;
664 }
665
666 private function formatMediaMeta($mediaPreviews, &$data, $mediaImages)
667 {
668 $giphyImages = Helper::getMediaByProvider($mediaImages, 'giphy');
669 $metaMediaItems = Helper::getMediaByProvider($this->request->get('meta.media_items', []), 'giphy');
670
671 if (count($mediaPreviews) === 1 && empty($giphyImages) && empty($metaMediaItems)) {
672 $mediaPreview = array_filter([
673 'is_uploaded' => true,
674 'image' => $mediaPreviews[0]['url'],
675 'type' => 'meta_data',
676 'width' => Arr::get($mediaPreviews[0], 'width'),
677 'height' => Arr::get($mediaPreviews[0], 'height')
678 ]);
679
680 $data['meta']['media_preview'] = $mediaPreview;
681 } elseif ($mediaPreviews) {
682 $data['meta']['media_items'] = $mediaPreviews;
683 }
684 }
685
686 private function processGiphyImages($requestData, &$data)
687 {
688 if (!isset($data['meta']['media_items'])) {
689 $data['meta']['media_items'] = null;
690 }
691
692 if ($metaMediaItems = Arr::get($requestData, 'meta.media_items', [])) {
693 $giphyMediaItems = Helper::getMediaByProvider($metaMediaItems, 'giphy');
694
695 if ($giphyMediaItems) {
696 $data['meta']['media_items'] = array_merge($giphyMediaItems, (array)$data['meta']['media_items']);
697 }
698 }
699
700 if ($giphyImages = Helper::getMediaByProvider(Arr::get($requestData, 'media_images', []), 'giphy')) {
701
702 foreach ($giphyImages as $giphy) {
703 $data['meta']['media_items'][] = [
704 'url' => $giphy['url'],
705 'type' => 'image',
706 'provider' => 'giphy'
707 ];
708 }
709 }
710 }
711
712 private function parseFirstUrl($messageRendered)
713 {
714 $firstUrl = FeedsHelper::findFirstUrl($messageRendered);
715
716 if ($firstUrl) {
717 $metaData = RemoteUrlParser::parse($firstUrl);
718 if ($metaData && !is_wp_error($metaData) && (!empty($metaData['image']) || !empty($metaData['html']))) {
719 return [
720 'media_preview' => $metaData
721 ];
722 }
723 }
724
725 return [];
726 }
727
728 private function saveMediaItems($feed, $mediaItems)
729 {
730 foreach ($mediaItems as $media) {
731 $media->feed_id = $feed->id;
732 $media->is_active = 1;
733 $media->object_source = 'feed';
734 $media->save();
735 }
736 }
737
738 private function handleMentions($feed, $mentions)
739 {
740 if ($mentions) {
741 do_action('fluent_community/feed_mentioned', $feed, $mentions['users']);
742 }
743 }
744
745 private function sanitizeAndValidateData($data)
746 {
747 if (empty($data['type'])) {
748 $data['type'] = 'text';
749 } else {
750 $data['type'] = sanitize_text_field($data['type']);
751 }
752
753 $this->validate($data, [
754 'message' => 'required',
755 'type' => 'required'
756 ]);
757
758 return FeedsHelper::sanitizeAndValidateData($data);
759 }
760
761 private function checkForDuplicatePost($userId, $message)
762 {
763 $message = trim($message);
764
765 $exist = Feed::where('user_id', $userId)
766 ->where('message', $message)
767 ->where('created_at', '>', gmdate('Y-m-d H:i:s', current_time('timestamp') - 7 * 24 * 60 * 60))
768 ->first();
769
770 if ($exist) {
771 return $this->sendError(['message' => 'No duplicate post please!']);
772 }
773
774 return false;
775 }
776
777 private function validateAndSetSpace($spaceSlug, $user)
778 {
779 if ($spaceSlug == '__self__post__') {
780 if (!Helper::hasGlobalPost()) {
781 throw new \Exception(__('Please select a valid space to post in', 'fluent-community'));
782 }
783
784 return null;
785 }
786
787 $space = Space::where('slug', $spaceSlug)->first();
788
789 if (!$space) {
790 throw new \Exception(__('Please select a valid space to post in', 'fluent-community'));
791 }
792
793 $user->verifySpacePermission('can_create_post', $space);
794
795 return $space->id;
796 }
797
798 public function deleteFeed(Request $request, $feed_id)
799 {
800 $feed = Feed::findOrFail($feed_id);
801
802 $user = User::find(get_current_user_id());
803 $user->canDeleteFeed($feed, true);
804 do_action('fluent_community/feed/before_deleted', $feed);
805 $feed->delete();
806
807 do_action('fluent_community/feed/deleted', $feed_id);
808
809 return [
810 'message' => 'Feed has been deleted successfully'
811 ];
812 }
813
814 public function deleteMediaPreview(Request $request, $feed_id)
815 {
816 $feed = Feed::findOrFail($feed_id);
817
818 $user = User::find(get_current_user_id());
819 $user->canDeleteFeed($feed, true);
820
821 do_action('fluent_community/feed/media_deleted', $feed->media);
822
823 $meta = $feed->meta;
824
825 $meta['media_preview'] = null;
826
827 $feed->meta = $meta;
828 $feed->save();
829
830 return [
831 'message' => __('Media preview has been removed successfully', 'fluent-community')
832 ];
833 }
834
835 public function handleMediaUpload(Request $request)
836 {
837 $allowedTypes = implode(
838 ',',
839 apply_filters('fluent_community/support_attachment_types', [
840 'image/jpeg',
841 'image/pjpeg',
842 'image/jpeg',
843 'image/pjpeg',
844 'image/png',
845 'image/gif',
846 'image/webp'
847 ])
848 );
849
850 $files = $this->validate($this->request->files(), [
851 'file' => 'mimetypes:' . $allowedTypes,
852 // 'source' => 'required|in:feed,avatar,comment,cover,space'
853 ], [
854 'file.mimetypes' => __('The file must be a image type.', 'fluent-community')
855 ]);
856
857 $uploadedFiles = FileSystem::put($files);
858
859 $file = $uploadedFiles[0];
860
861 $willWebPConvert = apply_filters('fluent_community/convert_image_to_webp', true, $file);
862
863 if ($request->get('resize') && $maxWidth = $request->get('max_width')) {
864 $upload_dir = wp_upload_dir();
865 $fileUrl = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $file['url']);
866 $editor = wp_get_image_editor($fileUrl);
867 if (!is_wp_error($editor) && $editor->get_size()['width'] > $maxWidth) {
868 // Current file extension
869 $ext = pathinfo($file['url'], PATHINFO_EXTENSION);
870 $imageExtensions = ['jpg', 'jpeg', 'png', 'gif'];
871
872 $willConvert = in_array($ext, $imageExtensions) && $willWebPConvert;
873
874 if ($willConvert) {
875 $imageExtensions = array_map(function ($ext) {
876 return '.' . $ext;
877 }, $imageExtensions);
878 $fileUrl = str_replace($imageExtensions, '.webp', $fileUrl);
879 $file['file'] = str_replace($imageExtensions, '.webp', $file['file']);
880 $file['url'] = str_replace($imageExtensions, '.webp', $file['url']);
881 $file['type'] = 'image/webp';
882 }
883
884 // resize the image
885 $editor->resize($maxWidth, null, false);
886 $editor->set_quality(90);
887 if ($willConvert) {
888 $editor->save($fileUrl, 'image/webp');
889 // remove original file now
890 wp_delete_file(str_replace('.webp', '.' . $ext, $fileUrl));
891 $file['is_converted'] = true;
892 } else {
893 $editor->save($fileUrl);
894 }
895
896 $file['meta'] = [
897 'width' => $editor->get_size()['width'],
898 'height' => $editor->get_size()['height']
899 ];
900 }
901 $file['path'] = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
902 } else {
903 $upload_dir = wp_upload_dir();
904 $file['path'] = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
905 }
906
907 if ($willWebPConvert && empty($file['is_converted']) && !$request->get('skip_convert')) {
908 $path = $file['path'];
909 $extension = pathinfo($path, PATHINFO_EXTENSION);
910
911 $convertFromExtensions = ['png', 'jpg', 'jpeg', 'gif'];
912 if ($extension != 'webp' && in_array($extension, $convertFromExtensions)) {
913 // Let's convert to webp
914 $editor = wp_get_image_editor($file['path']);
915 if (!is_wp_error($editor)) {
916 $orginalPath = $file['path'];
917 $file['path'] = str_replace('.' . $extension, '.webp', $file['path']);
918 $file['url'] = str_replace('.' . $extension, '.webp', $file['url']);
919 $file['type'] = 'image/webp';
920 $editor->save($file['path'], 'image/webp');
921 wp_delete_file($orginalPath);
922
923 $file['meta'] = [
924 'width' => $editor->get_size()['width'],
925 'height' => $editor->get_size()['height']
926 ];
927 }
928 }
929 }
930
931 $mediaData = [
932 'media_type' => $file['type'],
933 'driver' => 'local',
934 'media_path' => $file['path'],
935 'media_url' => $file['url'],
936 'settings' => Arr::get($file, 'meta', [])
937 ];
938
939 $mediaData = apply_filters('fluent_community/media_upload_data', $mediaData, $file);
940
941 if (is_wp_error($mediaData)) {
942 return $this->sendError([
943 'message' => $mediaData->get_error_message(),
944 'errors' => $mediaData->get_error_data()
945 ]);
946 }
947
948 if (!$mediaData) {
949 return $this->sendError([
950 'message' => 'Error while uploading the media'
951 ]);
952 }
953
954 // Let's create the media now
955 $media = Media::create($mediaData);
956
957 $mediaUrl = $media->public_url;
958
959 $mediaUrl = add_query_arg([
960 'media_key' => $media->media_key,
961 ], $mediaUrl);
962
963 return [
964 'media' => [
965 'url' => $mediaUrl,
966 'media_key' => $media->media_key,
967 'type' => $media->media_type,
968 'width' => Arr::get($media->settings, 'width'),
969 'height' => Arr::get($media->settings, 'height')
970 ]
971 ];
972 }
973
974 public function getTicker(Request $request)
975 {
976 do_action('fluent_communit/track_activity');
977 $lastLoadedTimeStamp = $request->get('last_fetched_timestamp');
978
979 //check if $lastLoadedTimeStamp is valid date
980 if (!$lastLoadedTimeStamp || (current_time('timestamp') - $lastLoadedTimeStamp) > HOUR_IN_SECONDS) {
981 return [
982 'last_fetched_timestamp' => current_time('timestamp'),
983 'error' => 'Invalid timestamp',
984 'given' => $lastLoadedTimeStamp
985 ];
986 }
987
988 $userId = get_current_user_id();
989 if (!$userId) {
990 return [
991 'last_fetched_timestamp' => current_time('timestamp'),
992 'error' => 'Invalid user'
993 ];
994 }
995
996 $newItemsCount = Feed::where('created_at', '>', date('Y-m-d H:i:s', $lastLoadedTimeStamp))
997 ->where('status', 'published')
998 ->byUserAccess(get_current_user_id())
999 ->count();
1000
1001 $notificationCount = NotificationSubscriber::unread()->where('user_id', $userId)->count();
1002
1003 return apply_filters('fluent_community/feed_ticker', [
1004 'last_fetched_timestamp' => current_time('timestamp'),
1005 'new_items_count' => $newItemsCount > 10 ? 10 : $newItemsCount,
1006 'unread_notification_count' => $notificationCount
1007 ]);
1008 }
1009
1010 public function getOembed(Request $request)
1011 {
1012 $url = $request->get('url');
1013 // check if the url is valid
1014 $metaData = RemoteUrlParser::parse($url);
1015
1016 if ($metaData && !is_wp_error($metaData)) {
1017 return [
1018 'oembed' => $metaData
1019 ];
1020 }
1021
1022 return $this->send([
1023 'message' => 'No oembed data found',
1024 'url' => $url
1025 ]);
1026 }
1027
1028 public function markdownToHtml(Request $request)
1029 {
1030 $message = trim(sanitize_textarea_field($request->get('text', '')));
1031
1032 $html = FeedsHelper::mdToHtml($message);
1033
1034 return [
1035 'html' => $html
1036 ];
1037 }
1038
1039 private function transformFeed(Feed $feed)
1040 {
1041 $userId = $this->getUserId();
1042 if ($userId) {
1043 $feed->has_user_react = $feed->hasUserReact($userId, 'like');
1044 $feed->bookmarked = $feed->hasUserReact($userId, 'bookmark');
1045
1046 $likedIds = FeedsHelper::getLikedIdsByUserFeedId($feed->id, get_current_user_id());
1047 $feed->comments->each(function ($comment) use ($likedIds) {
1048 if ($likedIds && in_array($comment->id, $likedIds)) {
1049 $comment->liked = 1;
1050 }
1051 });
1052
1053 if ($feed->content_type == 'survey') {
1054 $votedOptions = $feed->getSurveyCastsByUserId($userId);
1055
1056 if ($votedOptions) {
1057 $surveyConfig = Arr::get($feed->meta, 'survey_config', []);
1058 foreach ($surveyConfig['options'] as $index => $option) {
1059 if (in_array($option['slug'], $votedOptions)) {
1060 $surveyConfig['options'][$index]['voted'] = true;
1061 }
1062 }
1063 $meta = $feed->meta;
1064 $meta['survey_config'] = $surveyConfig;
1065 $feed->meta = $meta;
1066 }
1067 }
1068 }
1069
1070 return $feed;
1071 }
1072 }
1073