PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / trunk
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses vtrunk
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 / ReactionController.php

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

249 lines 8.5 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\Comment;
6 use FluentCommunity\App\Models\Feed;
7 use FluentCommunity\App\Models\Reaction;
8 use FluentCommunity\App\Models\XProfile;
9 use FluentCommunity\App\Services\FeedsHelper;
10 use FluentCommunity\App\Services\Helper;
11 use FluentCommunity\App\Services\ProfileHelper;
12 use FluentCommunity\Framework\Http\Request\Request;
13 use FluentCommunity\Framework\Support\Arr;
14
15 class ReactionController extends Controller
16 {
17 public function getByFeedId(Request $request)
18 {
19 $feedId = $request->getSafe('feed_id', 'intval');
20
21 if (!$feedId) {
22 return [
23 'reactions' => []
24 ];
25 }
26
27 $feed = Feed::withoutGlobalScopes()->byUserAccess(get_current_user_id())->findOrFail($feedId);
28
29 $reactions = $feed->reactions()
30 ->whereHas('xprofile')
31 ->with([
32 'xprofile' => function ($q) {
33 $q->select(ProfileHelper::getXProfilePublicFields());
34 }
35 ])
36 ->where('type', 'like')
37 ->distinct('user_id')
38 ->limit(100)
39 ->get(); // Todo: Add lazy loading in the future
40
41 return apply_filters('fluent_community/reactions_api_response', [
42 'reactions' => $reactions
43 ], $reactions, $request->all());
44 }
45
46 public function getByCommentId(Request $request)
47 {
48 $commentId = $request->getSafe('comment_id', 'intval');
49
50 if (!$commentId) {
51 return [
52 'reactions' => []
53 ];
54 }
55
56 $comment = Comment::findOrFail($commentId);
57
58 // Just validate the permission
59 Feed::withoutGlobalScopes()->byUserAccess(get_current_user_id())->findOrFail($comment->post_id);
60
61 $reactions = $comment
62 ->reactions()
63 ->whereHas('xprofile')
64 ->with([
65 'xprofile' => function ($q) {
66 $q->select(ProfileHelper::getXProfilePublicFields());
67 }
68 ])
69 ->where('type', 'like')
70 ->distinct('user_id')
71 ->limit(100)
72 ->get(); // Todo: Add lazy loading in the future
73
74 return apply_filters('fluent_community/reactions_api_response', [
75 'reactions' => $reactions
76 ], $reactions, $request->all());
77 }
78
79 public function addOrRemovePostReact(Request $request, $feed_id)
80 {
81 $currentUser = $this->getUser(true);
82 $feed = Feed::withoutGlobalScopes()->byUserAccess($currentUser->ID)->findOrFail($feed_id);
83 $type = $request->get('react_type', 'like');
84 $type = in_array($type, ['like', 'bookmark'], true) ? $type : 'like';
85 $willRemove = $request->get('remove');
86
87 if (!in_array($feed->status, FeedsHelper::getViewableByLinkStatuses(), true)) {
88 return $this->sendError([
89 'message' => __('This post is not published yet', 'fluent-community')
90 ]);
91 }
92
93 if (!$willRemove && (int) $currentUser->ID === (int) $feed->user_id && apply_filters('fluent_community/disable_self_post_react', false, $feed)) {
94 return $this->sendError([
95 'message' => __('You cannot react to your own post', 'fluent-community')
96 ]);
97 }
98
99 $react = Reaction::where('user_id', $currentUser->ID)
100 ->where('object_id', $feed->id)
101 ->where('type', $type)
102 ->objectType('feed')
103 ->first();
104
105 if ($willRemove) {
106 if ($react) {
107 $react->delete();
108 if ($type == 'like') {
109 $feed->reactions_count = $feed->reactions_count - 1;
110 $feed->timestamps = false; // Don't update the updated_at timestamp
111 $feed->save();
112 do_action('fluent_community/feed/react_removed', $feed);
113 }
114 }
115
116 return [
117 'message' => __('Reaction has been removed', 'fluent-community'),
118 'new_count' => $feed->reactions_count
119 ];
120 }
121
122 if ($react) {
123 return [
124 'message' => __('You have already reacted to this post', 'fluent-community'),
125 'new_count' => $feed->reactions_count
126 ];
127 }
128
129 // Serialize a user's concurrent reactions by locking their profile row,
130 // so parallel add requests cannot each insert a duplicate reaction. The
131 // like counter is updated atomically, so unrelated users never contend.
132 $react = Helper::dbTransaction(function () use ($feed, $currentUser, $type) {
133 XProfile::where('user_id', $currentUser->ID)->lockForUpdate()->first();
134
135 $react = Reaction::where('user_id', $currentUser->ID)
136 ->where('object_id', $feed->id)
137 ->where('type', $type)
138 ->objectType('feed')
139 ->first();
140
141 if ($react) {
142 return $react;
143 }
144
145 $react = Reaction::create([
146 'user_id' => $currentUser->ID,
147 'object_id' => $feed->id,
148 'type' => $type,
149 'object_type' => 'feed'
150 ]);
151
152 if ($type == 'like') {
153 // getQuery() so the atomic increment does not touch updated_at
154 Feed::withoutGlobalScopes()->where('id', $feed->id)->getQuery()->increment('reactions_count');
155 $feed->reactions_count = $feed->reactions_count + 1;
156 }
157
158 return $react;
159 });
160
161 if ($react->wasRecentlyCreated && $type == 'like') {
162 $react->load('xprofile');
163 do_action('fluent_community/feed/react_added', $react, $feed);
164 }
165
166 return [
167 'message' => __('Reaction has been added', 'fluent-community'),
168 'new_count' => $feed->reactions_count
169 ];
170 }
171
172 public function castSurveyVote(Request $request, $feed_id)
173 {
174 $userId = $this->getUserId();
175
176 $feed = Feed::byUserAccess($userId)
177 ->where('id', $feed_id)
178 ->first();
179
180 if (!$feed || $feed->content_type != 'survey' || !$userId) {
181 return $this->sendError([
182 'message' => __('Sorry! you do not have access to this post or invalid request', 'fluent-community')
183 ]);
184 }
185
186 $surveyConfig = Arr::get($feed->meta, 'survey_config', []);
187 if (empty($surveyConfig['options'])) {
188 return $this->sendError([
189 'message' => __('Sorry! This survey configuration is invalid', 'fluent-community')
190 ]);
191 }
192
193 $endDate = Arr::get($surveyConfig, 'end_date');
194 if ($endDate && strtotime($endDate) < current_time('timestamp')) {
195 return $this->sendError([
196 'message' => __('Sorry! This survey has ended', 'fluent-community')
197 ]);
198 }
199
200 $voteIndexes = (array) $request->get('vote_indexes', []);
201
202 $voteIndexes = array_values(array_map('sanitize_text_field', $voteIndexes));
203
204 $feed = FeedsHelper::castSurveyVote($voteIndexes, $feed, $userId);
205 $surveyConfig = Arr::get($feed->meta, 'survey_config', []);
206 $options = Arr::get($surveyConfig, 'options', []);
207 if (empty($options) || !is_array($options)) {
208 $options = [];
209 }
210
211 $votedOptions = $feed->getSurveyCastsByUserId($userId);
212
213 foreach ($options as $index => $option) {
214 if (in_array(Arr::get($option, 'slug'), $votedOptions, true)) {
215 $surveyConfig['options'][$index]['voted'] = true;
216 }
217 }
218
219 $surveyConfig = apply_filters('fluent_community/survey_config_response', $surveyConfig, $feed, $userId);
220
221 return [
222 'survey_config' => $surveyConfig
223 ];
224 }
225
226 public function getSurveyVoters($feedId, $optionSlug)
227 {
228 $currentUserId = get_current_user_id();
229
230 $feed = Feed::withoutGlobalScopes()->byUserAccess($currentUserId)->findOrFail($feedId);
231
232 $voters = $feed->surveyVotes()
233 ->where('object_type', $optionSlug)
234 ->whereHas('xprofile')
235 ->with([
236 'xprofile' => function ($q) {
237 $q->select(ProfileHelper::getXProfilePublicFields());
238 }
239 ])
240 ->limit(100)
241 ->get(); // Todo: Add lazy loading in the future
242
243 $data = [
244 'voters' => $voters
245 ];
246 return apply_filters('fluent_community/survey_voters_api_response', $data, $this->request->all());
247 }
248 }
249