PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.01
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
← All changes | app/Http/Controllers/SpaceController.php +400 -166 1.0.982.10.01 View file →
@@ -1,9 +1,8 @@
1 1 <?php
2 2
3 3 namespace FluentCommunity\App\Http\Controllers;
4 4
5 -use FluentCommunity\App\Functions\Utility;
6 5 use FluentCommunity\App\Models\Space;
7 6 use FluentCommunity\App\Models\Feed;
8 7 use FluentCommunity\App\Models\BaseSpace;
9 8 use FluentCommunity\App\Models\SpaceGroup;
@@ -8,11 +7,10 @@
8 7 use FluentCommunity\App\Models\BaseSpace;
9 8 use FluentCommunity\App\Models\SpaceGroup;
10 9 use FluentCommunity\App\Models\User;
11 10 use FluentCommunity\App\Services\CustomSanitizer;
12 -use FluentCommunity\App\Services\FeedsHelper;
13 11 use FluentCommunity\App\Services\Helper;
14 -use FluentCommunity\App\Services\LockscreenService;
12 +use FluentCommunity\App\Functions\Utility;
15 13 use FluentCommunity\App\Services\ProfileHelper;
16 14 use FluentCommunity\Framework\Http\Request\Request;
17 15 use FluentCommunity\App\Models\Comment;
18 16 use FluentCommunity\App\Models\Reaction;
@@ -28,60 +26,85 @@
28 26 $q->where('user_id', get_current_user_id());
29 27 })
30 28 ->get();
31 29
32 - return [
30 + $data = [
33 31 'spaces' => $spaces
34 32 ];
33 +
34 + return apply_filters('fluent_community/spaces_api_response', $data, $this->request->all());
35 35 }
36 36
37 37 public function create(Request $request)
38 38 {
39 39 $currentUser = $this->getUser(true);
40 - $currentUser->verifyCommunityPermission('community_admin');
41 40
42 41 $data = $request->get('space', []);
43 42
44 - if (empty($data['slug'])) {
45 - $data['slug'] = sanitize_title($data['title'], '');
46 - } else {
47 - $data['slug'] = sanitize_title($data['slug'], '');
48 - }
43 + $slug = Arr::get($data, 'slug') ?: Arr::get($data, 'title');
49 44
50 - $data['title'] = sanitize_text_field($data['title']);
51 - $data['privacy'] = sanitize_text_field($data['privacy']);
45 + $slug = preg_replace('/[^a-zA-Z0-9-_]/', '', $slug);
52 46
47 + $data['slug'] = sanitize_title($slug, '');
48 + $data['title'] = sanitize_text_field(Arr::get($data, 'title'));
49 + $data['privacy'] = sanitize_text_field(Arr::get($data, 'privacy'));
50 +
53 51 $this->validate($data, [
54 - 'title' => 'required',
55 - 'slug' => 'required|unique:fcom_spaces,slug',
56 - 'privacy' => 'required|in:public,private,secret',
57 - 'parent_id' => 'required|exists:fcom_spaces,id'
58 - ], [
59 - 'parent_id.required' => __('Please select a menu group', 'fluent-community')
52 + 'title' => 'required',
53 + 'slug' => 'unique:fcom_spaces,slug',
54 + 'privacy' => 'required|in:public,private,secret'
60 55 ]);
61 56
62 - $spaceGroup = SpaceGroup::findOrFail($data['parent_id']);
57 + if (Arr::get($data, 'settings.topic_required') === 'yes' && !array_filter((array)Arr::get($data, 'topic_ids', []))) {
58 + return $this->sendError([
59 + 'message' => __('Please select at least one topic when members are required to select a topic.', 'fluent-community')
60 + ]);
61 + }
63 62
63 + $spaceGroup = null;
64 + if (!empty($data['parent_id'])) {
65 + $spaceGroup = SpaceGroup::findOrFail($data['parent_id']);
66 + $serial = BaseSpace::query()->withoutGlobalScopes()->where('parent_id', $spaceGroup->id)->max('serial') + 1;
67 + } else {
68 + $serial = BaseSpace::query()->withoutGlobalScopes()->max('serial') + 1;
69 + }
70 +
71 + $settings = CustomSanitizer::santizeSpaceSettings(Arr::get($data, 'settings', []), $data['privacy']);
72 +
73 + if (is_wp_error($settings)) {
74 + return $this->sendError([
75 + 'message' => $settings->get_error_message()
76 + ]);
77 + }
78 +
64 79 $spaceData = apply_filters('fluent_community/space/create_data', [
65 80 'title' => sanitize_text_field($data['title']),
66 81 'slug' => $data['slug'],
67 82 'privacy' => $data['privacy'],
68 - 'description' => sanitize_textarea_field($data['description']),
69 - 'settings' => [
70 - 'restricted_post_only' => Arr::get($data, 'settings.restricted_post_only', 'no'),
71 - 'emoji' => CustomSanitizer::sanitizeEmoji(Arr::get($data, 'settings.emoji', '')),
72 - 'can_request_join' => Arr::get($data, 'settings.can_request_join', 'no'),
73 - 'custom_lock_screen' => Arr::get($data, 'settings.custom_lock_screen', 'no'),
74 - 'layout_style' => Arr::get($data, 'settings.layout_style', 'timeline'),
75 - 'show_sidebar' => Arr::get($data, 'settings.show_sidebar', 'yes'),
76 - 'shape_svg' => CustomSanitizer::sanitizeSvg(Arr::get($data, 'settings.shape_svg', '')),
77 - 'hide_members_count' => Arr::get($data, 'settings.hide_members_count', 'no') == 'yes' ? 'yes' : 'no',
78 - ],
79 - 'parent_id' => $spaceGroup->id,
80 - 'serial' => BaseSpace::where('parent_id', $spaceGroup->id)->max('serial') + 1
83 + 'description' => sanitize_textarea_field(Arr::get($data, 'description', '')),
84 + 'settings' => $settings,
85 + 'parent_id' => $spaceGroup ? $spaceGroup->id : null,
86 + 'serial' => $serial ?: 1
81 87 ]);
82 88
89 + $ogImage = Arr::get($data, 'settings.og_image', '');
90 + $ogMedia = null;
91 + if ($ogImage) {
92 + $ogMedia = Helper::getMediaFromUrl($ogImage);
93 + if ($ogMedia && !$ogMedia->is_active) {
94 + $spaceData['settings']['og_image'] = $ogMedia->public_url;
95 + }
96 + }
97 +
83 98 $space = Space::create($spaceData);
99 + if ($ogMedia && !$ogMedia->is_active) {
100 + $ogMedia->update([
101 + 'is_active' => true,
102 + 'user_id' => get_current_user_id(),
103 + 'sub_object_id' => $space->id,
104 + 'object_source' => 'space_og_image'
105 + ]);
106 + }
84 107
85 108 $imageTypes = ['cover_photo', 'logo'];
86 109 $metaData = [];
87 110 foreach ($imageTypes as $type) {
@@ -107,8 +130,15 @@
107 130 $space->members()->attach(get_current_user_id(), [
108 131 'role' => 'admin'
109 132 ]);
110 133
134 + if (Arr::has($data, 'topic_ids')) {
135 + $topicsConfig = Helper::getTopicsConfig();
136 + $topicIds = (array)Arr::get($data, 'topic_ids', []);
137 + $topicIds = array_slice($topicIds, 0, $topicsConfig['max_topics_per_space']);
138 + $space->syncTopics($topicIds);
139 + }
140 +
111 141 $currentUser->cacheAccessSpaces();
112 142 do_action('fluent_community/space/created', $space, $data);
113 143
114 144 return [
@@ -118,64 +148,130 @@
118 148 }
119 149
120 150 public function discover(Request $request)
121 151 {
122 -
152 + $start = microtime(true);
123 153 $currentUser = $this->getUser();
154 + $type = $request->getSafe('type');
155 + $search = $request->getSafe('search');
156 + $sortBy = $request->getSafe('sort_by', 'sanitize_text_field', 'alphabetical');
124 157
125 - $spaces = Space::orderBy('title', 'ASC')
126 - ->with(['space_pivot' => function ($q) {
127 - $q->where('user_id', get_current_user_id());
128 - }])
158 + $spaces = Space::with(['space_pivot' => function ($q) {
159 + $q->where('user_id', get_current_user_id());
160 + }])
129 161 ->where(function ($q) {
130 162 $q->whereHas('space_pivot', function ($q) {
163 + $q->where('user_id', get_current_user_id())
164 + ->where('status', 'active');
165 + })
166 + ->orWhereIn('privacy', ['public', 'private']);
167 + })
168 + ->when($type == 'joined', function ($q) {
169 + $q->whereHas('space_pivot', function ($q) {
131 170 $q->where('user_id', get_current_user_id());
171 + });
172 + })
173 + ->when($search, function ($q) use ($search) {
174 + $q->where('title', 'like', '%' . $search . '%');
175 + })
176 + ->when($sortBy == 'alphabetical', function ($q) {
177 + $q->orderBy('title', 'ASC');
178 + })
179 + ->when($sortBy == 'oldest', function ($q) {
180 + $q->orderBy('created_at', 'ASC');
181 + })
182 + ->when($sortBy == 'latest', function ($q) {
183 + $q->orderBy('created_at', 'DESC');
184 + })
185 + ->paginate();
186 +
187 + $memberCounts = $this->getActiveMemberCounts($spaces->pluck('id')->toArray());
188 +
189 + foreach ($spaces as $space) {
190 + $space->description_rendered = wpautop($space->description);
191 +
192 + $shouldHideMembersCount = Arr::get($space->settings, 'hide_members_count') == 'yes';
193 + $canViewMembers = $currentUser && $space->verifyUserPermisson($currentUser, 'can_view_members', false);
194 +
195 + if ($shouldHideMembersCount && !$canViewMembers) {
196 + $space->members_count = 0;
197 + continue;
198 + }
199 +
200 + $space->members_count = (int)Arr::get($memberCounts, $space->id, 0);
201 + }
202 +
203 + $data = [
204 + 'spaces' => $spaces,
205 + 'execution_time' => microtime(true) - $start
206 + ];
207 +
208 + return apply_filters('fluent_community/spaces_api_response', $data, $request->all());
209 + }
210 +
211 + public function getAllSpaces(Request $request)
212 + {
213 + $currentUser = $this->getUser();
214 +
215 + $spacesQuery = Space::query();
216 +
217 + if (!($currentUser && $currentUser->isCommunityModerator())) {
218 + $spacesQuery->where(function ($q) {
219 + $q->whereHas('space_pivot', function ($q) {
220 + $q->where('user_id', get_current_user_id())
221 + ->where('status', 'active');
132 222 })
133 223 ->orWhereIn('privacy', ['public', 'private']);
134 - })
135 - ->get();
224 + });
225 + }
226 + $spaces = $spacesQuery->paginate();
136 227
228 + $memberCounts = $this->getActiveMemberCounts($spaces->pluck('id')->toArray());
229 +
137 230 foreach ($spaces as $space) {
138 - if(Arr::get($space->settings, 'hide_members_count') == 'yes' && (!$currentUser || !$space->verifyUserPermisson($currentUser, 'can_view_members', false))) {
231 + $shouldHideMembersCount = Arr::get($space->settings, 'hide_members_count') == 'yes';
232 + $canViewMembers = $currentUser && $space->verifyUserPermisson($currentUser, 'can_view_members', false);
233 +
234 + if ($shouldHideMembersCount && !$canViewMembers) {
139 235 $space->members_count = 0;
140 236 continue;
141 237 }
142 238
143 - $space->members_count = $space->members()->wherePivot('status', 'active')->count();
239 + $space->members_count = (int)Arr::get($memberCounts, $space->id, 0);
240 +
241 + $space->formatSpaceData($currentUser);
242 + do_action_ref_array('fluent_community/space', [&$space]);
144 243 }
145 244
146 - return [
245 + return apply_filters('fluent_community/all_spaces_api_response', [
147 246 'spaces' => $spaces
148 - ];
247 + ], $request->all());
149 248 }
150 249
151 250 public function getBySlug(Request $request, $spaceSlug)
152 251 {
153 252 $user = $this->getUser();
154 - $space = Space::where('slug', $spaceSlug)
155 - ->firstOrFail();
253 + $space = Space::where('slug', $spaceSlug)->first();
156 254
157 - $space->permissions = $space->getUserPermissions($user);
158 - $space->description_rendered = FeedsHelper::mdToHtml($space->description);
159 - $space->membership = $space->getMembership(get_current_user_id());
160 - $space->topics = Utility::getTopicsBySpaceId($space->id);
255 + $userId = $user ? $user->ID : null;
161 256
162 - if (!Helper::isSiteAdmin()) {
163 - $space->lockscreen_config = LockscreenService::getLockscreenConfig($space, $space->membership);
257 + // A hidden secret space must be indistinguishable from a non-existent one, so its
258 + // existence cannot be enumerated by slug. Both return an identical 404.
259 + if (!$space || ($space->privacy == 'secret' && !$space->getMembership($userId) && !$space->isAdmin($userId, true))) {
260 + return $this->sendError([
261 + 'message' => __('Space not found', 'fluent-community')
262 + ], 404);
164 263 }
165 264
166 - if ($space->privacy == 'secret' && !$space->membership) {
167 - return $this->sendError([
168 - 'message' => __('You are not allowed to view this space', 'fluent-community'),
169 - 'error_type' => 'restricted'
170 - ]);
171 - }
265 + $space = $space->formatSpaceData($user);
172 266
173 267 do_action_ref_array('fluent_community/space', [&$space]);
174 268
175 - return [
269 + $data = [
176 270 'space' => $space
177 271 ];
272 +
273 + return apply_filters('fluent_community/space_api_response', $data, $request->all());
178 274 }
179 275
180 276 public function patchBySlug(Request $request, $slug)
181 277 {
@@ -183,42 +279,57 @@
183 279 ->first();
184 280
185 281 if (!$space) {
186 282 return $this->sendError([
187 - 'message' => 'Space not found'
283 + 'message' => __('Space not found', 'fluent-community')
188 284 ]);
189 285 }
190 286
191 - $space->verifyUserPermisson($this->getUser(), 'community_admin');
287 + $data = $request->get('data', []);
192 288
193 - $data = $request->get('data', []);
289 + if (Arr::has($data, 'title') && !trim(sanitize_text_field(Arr::get($data, 'title', '')))) {
290 + return $this->sendError([
291 + 'message' => __('Space title is required.', 'fluent-community')
292 + ]);
293 + }
194 294
195 - if (!empty($data['title'])) {
196 - $taken = Space::where('title', $data['title'])
295 + if (!empty($data['slug'])) {
296 + $taken = Space::where('slug', $data['slug'])
197 297 ->where('id', '!=', $space->id)
198 298 ->first();
299 +
199 300 if ($taken) {
200 301 return $this->sendError([
201 - 'message' => 'Space title is already taken. Please use a different title'
302 + 'message' => __('Slug is already taken. Please use a different slug', 'fluent-community')
202 303 ]);
203 304 }
204 305 }
205 306
307 + $topicRequired = Arr::has($data, 'settings.topic_required')
308 + ? Arr::get($data, 'settings.topic_required')
309 + : Arr::get($space->settings, 'topic_required');
310 +
311 + if ($topicRequired === 'yes') {
312 + $topicIds = Arr::has($data, 'topic_ids')
313 + ? (array)Arr::get($data, 'topic_ids', [])
314 + : array_column(Utility::getTopicsBySpaceId($space->id), 'id');
315 +
316 + if (!array_filter($topicIds)) {
317 + return $this->sendError([
318 + 'message' => __('Please select at least one topic when members are required to select a topic.', 'fluent-community')
319 + ]);
320 + }
321 + }
322 +
206 323 $mediaTypes = ['cover_photo', 'logo'];
207 324 foreach ($mediaTypes as $type) {
208 325 if (!empty($data[$type])) {
209 326 $media = Helper::getMediaFromUrl($data[$type]);
210 - if (!$media) {
327 + if (!$media || $media->is_active) {
211 328 unset($data[$type]);
212 329 continue;
213 330 }
214 331
215 - if (!$media || $media->is_active) {
216 - return $this->sendError([
217 - 'message' => 'Invalid media image. Please upload a new one.'
218 - ]);
219 - }
220 -
221 332 $data[$type] = $media->public_url;
222 333
223 334 $media->update([
224 335 'is_active' => true,
@@ -232,22 +343,53 @@
232 343 }
233 344
234 345 $data = apply_filters('fluent_community/space/update_data', $data, $space);
235 346
236 - $space->updateCustomData($data, true);
347 + if (isset($data['parent_id']) && !$data['parent_id']) {
348 + $data['parent_id'] = '';
349 + }
237 350
351 + $space = $space->updateCustomData($data, true);
352 +
353 + if (is_wp_error($space)) {
354 + return $this->sendError([
355 + 'message' => $space->get_error_message()
356 + ]);
357 + }
358 +
238 359 if (Arr::has($data, 'topic_ids')) {
360 + $topicsConfig = Helper::getTopicsConfig();
239 361 $topicIds = (array)Arr::get($data, 'topic_ids', []);
362 + $topicIds = array_slice($topicIds, 0, $topicsConfig['max_topics_per_space']);
240 363 $space->syncTopics($topicIds);
241 364 }
242 365
366 + do_action('fluent_community/space/updated', $space, $data);
367 + $slugUpdated = $slug != $space->slug;
368 +
369 + $metaSettings = $request->get('meta_settings', []);
370 + if($metaSettings) {
371 + foreach ($metaSettings as $metaProvider => $metaData) {
372 + do_action('fluent_community/space/update_meta_settings_'.$metaProvider, $metaData, $space);
373 + }
374 + }
375 +
243 376 return [
244 - 'message' => __('Space has been updated', 'fluent-community')
377 + 'message' => __('Settings have been updated', 'fluent-community'),
378 + 'redirect_url' => $slugUpdated ? $space->getPermalink() : ''
245 379 ];
246 380 }
247 381
382 + public function patchById(Request $request, $id)
383 + {
384 + $space = Space::findOrFail($id);
385 +
386 + return $this->patchBySlug($request, $space->slug);
387 + }
388 +
248 389 public function getMembers(Request $request, $slug)
249 390 {
391 + /** @var Space $space */
250 392 $space = Space::where('slug', $slug)
251 393 ->firstOrFail();
252 394
253 395 $user = $this->getUser();
@@ -257,8 +399,9 @@
257 399 'message' => __('You are not allowed to view members of this space', 'fluent-community'),
258 400 'permission_failed' => true
259 401 ]);
260 402 }
403 +
261 404 $search = $request->getSafe('search', 'sanitize_text_field');
262 405
263 406 $pendingCount = 0;
264 407 if ($user && $user->can('can_add_member', $space)) {
@@ -277,30 +420,48 @@
277 420 }])
278 421 ->where('status', 'pending')
279 422 ->paginate();
280 423
281 - return [
424 + return apply_filters('fluent_community/space_members_api_response', [
282 425 'members' => $pendingRequests,
283 426 'pending_count' => $pendingCount
284 - ];
427 + ], $pendingRequests, $request->all());
285 428 }
286 429 }
287 430
431 + $defaultDirections = [
432 + 'last_activity' => 'DESC',
433 + 'display_name' => 'ASC',
434 + 'created_at' => 'DESC',
435 + ];
436 +
437 + $sortBy = $request->getSafe('sort_by', 'sanitize_text_field', 'created_at');
438 + $sortColumn = in_array($sortBy, array_keys($defaultDirections), true) ? $sortBy : 'created_at';
439 + $sortDir = strtoupper($request->getSafe('sort_dir', 'sanitize_text_field', ''));
440 + $sortDirection = in_array($sortDir, ['ASC', 'DESC'], true) ? $sortDir : ($sortColumn === 'created_at' ? 'ASC' : $defaultDirections[$sortColumn]);
441 +
442 + $profileSort = $sortColumn !== 'created_at';
443 + $orderColumn = $profileSort ? 'fcom_xprofile.' . $sortColumn : 'fcom_space_user.created_at';
444 +
288 445 $spaceMembers = SpaceUserPivot::bySpace($space->id)
289 446 ->whereHas('xprofile', function ($q) use ($search) {
290 - return $q->searchBy($search)
291 - ->where('status', 'active');
447 + $q->searchBy($search)->where('status', 'active');
292 448 })
449 + ->where('fcom_space_user.status', 'active')
293 450 ->with(['xprofile' => function ($q) {
294 451 $q->select(ProfileHelper::getXProfilePublicFields());
295 452 }])
296 - ->where('status', 'active')
453 + ->when($profileSort, function ($q) {
454 + $q->join('fcom_xprofile', 'fcom_xprofile.user_id', '=', 'fcom_space_user.user_id')
455 + ->select('fcom_space_user.*');
456 + })
457 + ->orderBy($orderColumn, $sortDirection)
297 458 ->paginate();
298 459
299 - return [
460 + return apply_filters('fluent_community/space_members_api_response', [
300 461 'members' => $spaceMembers,
301 462 'pending_count' => $pendingCount
302 - ];
463 + ], $spaceMembers, $request->all());
303 464 }
304 465
305 466 public function join(Request $request, $slug)
306 467 {
@@ -307,9 +468,9 @@
307 468 $space = Space::where('slug', $slug)->first();
308 469
309 470 if (!$space) {
310 471 return $this->sendError([
311 - 'message' => 'Space not found'
472 + 'message' => __('Space not found', 'fluent-community')
312 473 ]);
313 474 }
314 475
315 476 $user = $this->getUser();
@@ -317,22 +478,23 @@
317 478 $membership = $space->getMembership(get_current_user_id());
318 479
319 480 if ($membership) {
320 481 return $this->sendError([
321 - 'message' => 'You are already a member of this space. Please reload this page'
482 + 'message' => __('You are already a member of this space. Please reload this page', 'fluent-community')
322 483 ]);
323 484 }
324 485
325 486 $roles = $user->getCommunityRoles();
487 + $isPrivileged = !!array_intersect($roles, ['admin', 'moderator']);
326 488
327 - if (!$roles && $space->privacy == 'secret') {
489 + if (!$isPrivileged && $space->privacy == 'secret') {
328 490 return $this->sendError([
329 - 'message' => 'You are not allowed to join this space'
491 + 'message' => __('You are not allowed to join this space', 'fluent-community')
330 492 ]);
331 493 }
332 494
333 495 $status = 'active';
334 - if (!$roles) {
496 + if (!$isPrivileged) {
335 497 if ($space->privacy != 'public') {
336 498 $status = apply_filters('fluent_community/space/join_status_for_private', 'pending', $space, $user);
337 499
338 500 if (!in_array($status, ['pending', 'active'])) {
@@ -372,9 +534,9 @@
372 534 $space = Space::where('slug', $slug)->first();
373 535
374 536 if (!$space) {
375 537 return $this->sendError([
376 - 'message' => 'Space not found'
538 + 'message' => __('Space not found', 'fluent-community')
377 539 ]);
378 540 }
379 541
380 542 $membership = $space->getMembership($user->ID);
@@ -380,9 +542,9 @@
380 542 $membership = $space->getMembership($user->ID);
381 543
382 544 if (!$membership) {
383 545 return $this->sendError([
384 - 'message' => 'You are not a member of this community'
546 + 'message' => __('You are not a member of this community', 'fluent-community')
385 547 ]);
386 548 }
387 549
388 550 Helper::removeFromSpace($space, $user->ID, 'self');
@@ -391,24 +553,18 @@
391 553 'message' => __('You have left this space', 'fluent-community')
392 554 ];
393 555 }
394 556
395 - public function delete(Request $request, $slug)
557 + public function deleteBySlug(Request $request, $slug)
396 558 {
397 559 $space = Space::where('slug', $slug)->first();
398 560
399 561 if (!$space) {
400 562 return $this->sendError([
401 - 'message' => 'Space not found'
563 + 'message' => __('Space not found', 'fluent-community')
402 564 ]);
403 565 }
404 566
405 - if (!Helper::isSiteAdmin()) {
406 - return $this->sendError([
407 - 'message' => 'You are not allowed to delete this community'
408 - ]);
409 - }
410 -
411 567 do_action('fluent_community/space/before_delete', $space);
412 568
413 569 Comment::whereHas('post', function ($q) use ($space) {
414 570 $q->where('space_id', $space->id);
@@ -431,8 +587,15 @@
431 587 'message' => __('Space has been deleted successfully', 'fluent-community')
432 588 ];
433 589 }
434 590
591 + public function deleteById(Request $request, $id)
592 + {
593 + $space = Space::findOrFail($id);
594 +
595 + return $this->deleteBySlug($request, $space->slug);
596 + }
597 +
435 598 public function addMember(Request $request, $slug)
436 599 {
437 600 $space = Space::where('slug', $slug)->first();
438 601
@@ -437,9 +600,9 @@
437 600 $space = Space::where('slug', $slug)->first();
438 601
439 602 if (!$space) {
440 603 return $this->sendError([
441 - 'message' => 'Space not found'
604 + 'message' => __('Space not found', 'fluent-community')
442 605 ]);
443 606 }
444 607
445 608 $this->validate($request->all(), [
@@ -447,9 +610,10 @@
447 610 ]);
448 611
449 612 $userId = $request->get('user_id');
450 613 $targetUser = User::findOrFail($userId);
451 - $xprofile = $targetUser->syncXProfile();
614 + $targetUser->syncXProfile();
615 + $xprofile = $targetUser->xprofile;
452 616
453 617 if ($xprofile && $xprofile->status != 'active') {
454 618 return $this->sendError([
455 619 'message' => __('Selected user is not active', 'fluent-community')
@@ -455,16 +619,15 @@
455 619 'message' => __('Selected user is not active', 'fluent-community')
456 620 ]);
457 621 }
458 622
459 - $admin = User::find(get_current_user_id());
460 - $admin->verifySpacePermission('can_add_member', $space);
461 -
462 623 $pivot = SpaceUserPivot::bySpace($space->id)
463 624 ->byUser($userId)
464 625 ->first();
465 626
466 - $role = $request->get('role', 'member');
627 + $allowedRoles = ['member', 'moderator', 'admin'];
628 + $requestedRole = $request->get('role', 'member');
629 + $role = in_array($requestedRole, $allowedRoles, true) ? $requestedRole : 'member';
467 630
468 631 if ($pivot) {
469 632 if ($pivot->status == 'active') {
470 633 if ($role != $pivot->role) {
@@ -473,14 +636,14 @@
473 636
474 637 do_action('fluent_community/space/member/role_updated', $space, $pivot);
475 638
476 639 return [
477 - 'message' => 'Member role updated'
640 + 'message' => __('Member role updated', 'fluent-community')
478 641 ];
479 642 }
480 643
481 644 return $this->sendError([
482 - 'message' => 'Selected user is already a member of this community'
645 + 'message' => __('Selected user is already a member of this community', 'fluent-community')
483 646 ]);
484 647 }
485 648
486 649 $pivot->status = 'active';
@@ -491,9 +654,9 @@
491 654 do_action('fluent_community/space/member/role_updated', $space, $pivot);
492 655 }
493 656
494 657 return [
495 - 'message' => 'Member approved'
658 + 'message' => __('Member approved', 'fluent-community')
496 659 ];
497 660 }
498 661
499 662 $space->members()->attach($userId, [
@@ -505,9 +668,9 @@
505 668
506 669 do_action('fluent_community/space/joined', $space, $userId, 'by_admin');
507 670
508 671 return [
509 - 'message' => 'User has been added to this community'
672 + 'message' => __('User has been added to this Space', 'fluent-community')
510 673 ];
511 674 }
512 675
513 676 public function removeMember(Request $request, $slug)
@@ -515,17 +678,14 @@
515 678 $space = Space::where('slug', $slug)->first();
516 679
517 680 if (!$space) {
518 681 return $this->sendError([
519 - 'message' => 'Space not found'
682 + 'message' => __('Space not found', 'fluent-community')
520 683 ]);
521 684 }
522 685
523 686 $userId = $request->get('user_id');
524 687
525 - $admin = User::find(get_current_user_id());
526 - $admin->verifySpacePermission('can_remove_member', $space);
527 -
528 688 $pivot = SpaceUserPivot::bySpace($space->id)
529 689 ->byUser($userId)
530 690 ->first();
531 691
@@ -530,9 +690,9 @@
530 690 ->first();
531 691
532 692 if (!$pivot) {
533 693 return $this->sendError([
534 - 'message' => 'Selected user is not a member of this community'
694 + 'message' => __('Selected user is not a member of this community', 'fluent-community')
535 695 ]);
536 696 }
537 697
538 698 $pivot->delete();
@@ -560,25 +720,53 @@
560 720
561 721 $isMod = $currentUser->isCommunityModerator() && current_user_can('list_users');
562 722
563 723 $spaceId = $request->get('space_id');
724 + $space = Space::findOrFail($spaceId);
564 725
726 + if (!$space->verifyUserPermisson($currentUser, 'can_add_member', false)) {
727 + return $this->sendError([
728 + 'message' => __('You are not allowed to add members to this space', 'fluent-community')
729 + ]);
730 + }
731 +
565 732 $selects = ['ID', 'display_name'];
566 -
567 733 if ($isMod) {
568 734 $selects[] = 'user_email';
569 735 }
570 736
571 - $users = User::whereDoesntHave('spaces', function ($q) use ($spaceId) {
572 - return $q->where('space_id', $spaceId);
573 - })
574 - ->select($selects)
575 - ->searchBy($request->get('search'))
576 - ->paginate();
737 + $userQuery = User::select(['ID'])
738 + ->whereDoesntHave('space_pivot', function ($q) use ($space) {
739 + $q->where('space_id', $space->id);
740 + })
741 + ->limit(100)
742 + ->searchBy($request->getSafe('search'));
577 743
578 - return [
744 + if (is_multisite()) {
745 + global $wpdb;
746 + $blogId = get_current_blog_id();
747 + $blogPrefix = $wpdb->get_blog_prefix($blogId);
748 + $userQuery->whereHas('usermeta', function ($q) use ($blogPrefix) {
749 + $q->where('meta_key', $blogPrefix . 'capabilities');
750 + });
751 + }
752 +
753 + $userIds = $userQuery->get()
754 + ->pluck('ID')
755 + ->toArray();
756 +
757 + if ($userIds) {
758 + update_meta_cache('user', $userIds);
759 + }
760 +
761 + $users = User::select($selects)
762 + ->whereIn('ID', $userIds)
763 + ->paginate(100);
764 +
765 + $data = [
579 766 'users' => $users
580 767 ];
768 + return apply_filters('fluent_community/space_non_members_api_response', $data, $request->all());
581 769 }
582 770
583 771 public function updateLinks(Request $request, $slug)
584 772 {
@@ -585,14 +773,12 @@
585 773 $space = Space::where('slug', $slug)->first();
586 774
587 775 if (!$space) {
588 776 return $this->sendError([
589 - 'message' => 'Space not found'
777 + 'message' => __('Space not found', 'fluent-community')
590 778 ]);
591 779 }
592 780
593 - $space->verifyUserPermisson($this->getUser(), 'community_admin');
594 -
595 781 $links = $request->get('links', []);
596 782
597 783 $links = array_map(function ($link) {
598 784 return CustomSanitizer::santizeLinkItem($link);
@@ -610,13 +796,16 @@
610 796 }
611 797
612 798 public function getSpaceGroups(Request $request)
613 799 {
614 - $user = $this->getUser(true);
615 - if (!$user->isCommunityModerator()) {
616 - return $this->sendError([
617 - 'message' => 'You are not allowed to create space group'
618 - ]);
800 + if ($request->get('options_only')) {
801 + $groups = SpaceGroup::orderBy('serial', 'ASC')
802 + ->select(['id', 'title'])
803 + ->get();
804 +
805 + return [
806 + 'groups' => $groups
807 + ];
619 808 }
620 809
621 810 $user = $this->getUser();
622 811
@@ -624,25 +813,40 @@
624 813
625 814 foreach ($groups as $group) {
626 815 foreach ($group->spaces as $space) {
627 816 $space->permalink = $space->getPermalink();
817 + if ($space->type === 'community') {
818 + $space = $space->formatSpaceData($user);
819 + } else {
820 + $space->topics = Utility::getTopicsBySpaceId($space->id);
821 + }
628 822 }
629 823 }
630 824
631 - return [
632 - 'groups' => $groups
825 + $orphanedSpaces = BaseSpace::withoutGlobalScopes()
826 + ->whereNull('parent_id')
827 + ->whereIn('type', ['community', 'course'])
828 + ->orderBy('title', 'ASC')
829 + ->get();
830 +
831 + foreach ($orphanedSpaces as $space) {
832 + $space->permalink = $space->getPermalink();
833 + if ($space->type === 'community') {
834 + $space = $space->formatSpaceData($user);
835 + } else {
836 + $space->topics = Utility::getTopicsBySpaceId($space->id);
837 + }
838 + }
839 +
840 + $data = [
841 + 'groups' => $groups,
842 + 'orphaned_spaces' => $orphanedSpaces
633 843 ];
844 + return apply_filters('fluent_community/space_groups_api_response', $data, $request->all());
634 845 }
635 846
636 847 public function createSpaceGroup(Request $request)
637 848 {
638 - $user = $this->getUser(true);
639 - if (!$user->isCommunityModerator()) {
640 - return $this->sendError([
641 - 'message' => 'You are not allowed to create space group'
642 - ]);
643 - }
644 -
645 849 $data = $request->all();
646 850
647 851 $this->validate($data, [
648 852 'title' => 'required|unique:fcom_spaces,title',
@@ -648,13 +852,28 @@
648 852 'title' => 'required|unique:fcom_spaces,title',
649 853 'slug' => 'required|unique:fcom_spaces,slug'
650 854 ]);
651 855
856 + $title = sanitize_text_field(Arr::get($data, 'title', ''));
857 + $slug = sanitize_title(Arr::get($data, 'slug', ''));
858 + $desc = sanitize_textarea_field(Arr::get($data, 'description', ''));
652 859
860 + if (!$title) {
861 + return $this->sendError([
862 + 'message' => __('Please enter a valid group title.', 'fluent-community')
863 + ]);
864 + }
865 +
866 + if (!$slug) {
867 + return $this->sendError([
868 + 'message' => __('Please enter a valid group slug.', 'fluent-community')
869 + ]);
870 + }
871 +
653 872 $formattedData = [
654 - 'title' => sanitize_text_field($data['title']),
655 - 'slug' => sanitize_title($data['slug']),
656 - 'description' => sanitize_textarea_field($data['description']),
873 + 'title' => $title,
874 + 'slug' => $slug,
875 + 'description' => $desc,
657 876 'status' => 'active',
658 877 'type' => 'space_group',
659 878 'settings' => [
660 879 'always_show_spaces' => Arr::get($data, 'settings.always_show_spaces', 'yes'),
@@ -671,15 +890,8 @@
671 890 }
672 891
673 892 public function updateSpaceGroup(Request $request, $groupId)
674 893 {
675 - $user = $this->getUser();
676 - if (!$user || !$user->isCommunityModerator()) {
677 - return $this->sendError([
678 - 'message' => 'You are not allowed to create space group'
679 - ]);
680 - }
681 -
682 894 $group = SpaceGroup::findOrFail($groupId);
683 895 $data = $request->all();
684 896
685 897 $this->validate($data, [
@@ -691,9 +903,9 @@
691 903 ->first();
692 904
693 905 if ($taken) {
694 906 return $this->sendError([
695 - 'message' => 'The title is already taken. Please use a different title'
907 + 'message' => __('The title is already taken. Please use a different title', 'fluent-community')
696 908 ]);
697 909 }
698 910
699 911 $formattedData = [
@@ -708,9 +920,9 @@
708 920
709 921 $group->fill($formattedData)->save();
710 922
711 923 return [
712 - 'message' => __('Space group has been created updated', 'fluent-community'),
924 + 'message' => __('Space group has been updated', 'fluent-community'),
713 925 'group' => $group
714 926 ];
715 927 }
716 928
@@ -715,21 +927,13 @@
715 927 }
716 928
717 929 public function deleteSpaceGroup(Request $request, $groupId)
718 930 {
719 -
720 - $user = $this->getUser();
721 - if (!$user || !$user->isCommunityModerator()) {
722 - return $this->sendError([
723 - 'message' => 'You are not allowed to create space group'
724 - ]);
725 - }
726 -
727 931 $group = SpaceGroup::findOrFail($groupId);
728 932
729 933 if (!$group->spaces->isEmpty()) {
730 934 return $this->sendError([
731 - 'message' => 'You can not delete this group. It has spaces'
935 + 'message' => __('You cannot delete this group. It has spaces', 'fluent-community')
732 936 ]);
733 937 }
734 938
735 939 $group->delete();
@@ -780,9 +984,9 @@
780 984 $space = BaseSpace::withoutGlobalScopes()->findOrFail($spaceId);
781 985 $group = SpaceGroup::findOrFail($groupId);
782 986
783 987 $space->update([
784 - 'parent_id' => $groupId
988 + 'parent_id' => $group->id
785 989 ]);
786 990
787 991 return [
788 992 'message' => __('Space has been moved successfully', 'fluent-community')
@@ -790,28 +994,58 @@
790 994 }
791 995
792 996 public function getLockScreenSettings(Request $request, $spaceSlug)
793 997 {
794 - $space = Space::where('slug', $spaceSlug)->firstOrFail();
998 + /** @var Space $space */
999 + $space = Space::where('slug', $spaceSlug)->first();
1000 +
1001 + $userId = $this->getUser() ? $this->getUser()->ID : null;
1002 +
1003 + if (!$space || ($space->privacy == 'secret' && !$space->getMembership($userId) && !$space->isAdmin($userId, true))) {
1004 + return $this->sendError([
1005 + 'message' => __('Space not found', 'fluent-community')
1006 + ], 404);
1007 + }
1008 +
795 1009 $lockscreen = $space->getLockscreen();
796 1010
1011 + $lockscreen = apply_filters('fluent_community/get_lockscreen_settings', $lockscreen, $space);
1012 +
797 1013 return [
798 1014 'lockscreen' => $lockscreen
799 1015 ];
800 1016 }
801 1017
802 - public function updateLockscreenSettings(Request $request, $spaceSlug)
1018 + public function getMetaSettings(Request $request, $spaceSlug)
803 1019 {
804 1020 $space = Space::where('slug', $spaceSlug)->firstOrFail();
1021 + $metaSettings = apply_filters('fluent_community/space/meta_fields', [], $space);
805 1022
806 - $settingFields = $request->get('lockscreen', []);
1023 + if (!$metaSettings) {
1024 + return [
1025 + 'meta_settings' => null
1026 + ];
1027 + }
807 1028
808 - $formattedFields = LockscreenService::formatLockscreenFields($settingFields);
1029 + return [
1030 + 'meta_settings' => $metaSettings
1031 + ];
1032 + }
809 1033
810 - $space->setLockscreen($formattedFields);
1034 + private function getActiveMemberCounts($spaceIds)
1035 + {
1036 + if (!$spaceIds) {
1037 + return [];
1038 + }
811 1039
812 - return [
813 - 'message' => 'Lockscreen settings have been updated successfully.',
814 - 'community' => $space
815 - ];
1040 + return SpaceUserPivot::whereIn('space_id', $spaceIds)
1041 + ->where('status', 'active')
1042 + ->whereHas('xprofile', function ($q) {
1043 + $q->where('status', 'active');
1044 + })
1045 + ->whereHas('user')
1046 + ->selectRaw('space_id, COUNT(*) as total')
1047 + ->groupBy('space_id')
1048 + ->pluck('total', 'space_id')
1049 + ->toArray();
816 1050 }
817 1051 }