PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.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 1.1.0 All 77 releases
← All changes | Modules/Course/Http/Controllers/CourseAdminController.php +337 -117 2.4.012.10.0 View file →
@@ -28,14 +28,28 @@
28 28 public function getCourses(Request $request)
29 29 {
30 30 $user = $this->getUser();
31 31
32 - $courses = Course::searchBy($request->getSafe('search'))
32 + $status = $request->getSafe('status');
33 + $sortBy = $request->getSafe('sort_by', 'sanitize_text_field', 'latest');
34 + $topicSlug = $request->getSafe('topic_slug');
35 +
36 + $query = Course::searchBy($request->getSafe('search'))
33 37 ->byAdminAccess($user->ID)
34 - ->orderBy('id', 'DESC')
35 - ->with(['owner'])
36 - ->paginate();
38 + ->byPostTopic($topicSlug);
37 39
40 + if ($status && in_array($status, [ 'published', 'draft' ])) {
41 + $query->where('status', $status);
42 + }
43 +
44 + if ($sortBy === 'alphabetical') {
45 + $query->orderBy('title', 'ASC');
46 + } else {
47 + $query->orderBy('created_at', 'DESC');
48 + }
49 +
50 + $courses = $query->with([ 'owner' ])->paginate();
51 +
38 52 foreach ($courses as $course) {
39 53 $course->students_count = $course->students()->count();
40 54 if (!$course->cover_photo) {
41 55 $course->cover_photo = FLUENT_COMMUNITY_PLUGIN_URL . 'assets/images/course-placeholder.jpg';
@@ -44,9 +58,10 @@
44 58 $course->lessonsCount = CourseLesson::where('space_id', $course->id)->count();
45 59 }
46 60
47 61 $data = [
48 - 'courses' => $courses
62 + 'courses' => $courses,
63 + 'course_categories' => $request->get('with_categories') ? CourseHelper::getCourseCategories() : [],
49 64 ];
50 65
51 66 return apply_filters('fluent_community/admin_courses_api_response', $data, $request->all());
52 67 }
@@ -56,9 +71,9 @@
56 71 $this->validate($request->all(), [
57 72 'title' => 'required',
58 73 'description' => 'required',
59 74 'privacy' => 'required|in:public,private,secret',
60 - 'course_type' => 'required|in:self_paced,structured,scheduled'
75 + 'course_type' => 'required|in:self_paced,structured,scheduled',
61 76 ]);
62 77
63 78 $parentId = $request->get('parent_id');
64 79 if ($parentId) {
@@ -67,12 +82,12 @@
67 82 $serial = BaseSpace::max('serial') + 1;
68 83 }
69 84
70 85 $courseData = [
71 - 'parent_id' => $request->get('parent_id') ?: NULL,
86 + 'parent_id' => $request->get('parent_id') ?: null, // phpcs:ignore Universal.Operators.DisallowShortTernary.Found
72 87 'title' => $request->getSafe('title', 'sanitize_text_field'),
73 88 'privacy' => $request->get('privacy'),
74 - 'description' => wp_kses_post($request->get('description')),
89 + 'description' => wp_kses_post((string) $request->get('description', '')),
75 90 'status' => $request->get('status', 'draft'),
76 91 'settings' => [
77 92 'course_type' => $request->get('course_type'),
78 93 'emoji' => CustomSanitizer::sanitizeEmoji($request->get('settings.emoji', '')),
@@ -79,17 +94,18 @@
79 94 'shape_svg' => CustomSanitizer::sanitizeSvg($request->get('settings.shape_svg', '')),
80 95 'disable_comments' => $request->get('settings.disable_comments') === 'yes' ? 'yes' : 'no',
81 96 'hide_members_count' => $request->get('settings.hide_members_count') === 'yes' ? 'yes' : 'no',
82 97 'course_layout' => $request->get('settings.course_layout') === 'modern' ? 'modern' : 'classic',
83 - 'course_details' => CustomSanitizer::unslashMarkdown(trim($request->get('settings.course_details'))),
98 + 'course_details' => CustomSanitizer::unslashMarkdown(trim((string) $request->get('settings.course_details', ''))),
84 99 'hide_instructor_view' => $request->get('settings.hide_instructor_view') === 'yes' ? 'yes' : 'no',
85 - 'show_instructor_students_count' => $request->get('settings.show_instructor_students_count') === 'yes' ? 'yes' : 'no'
100 + 'show_instructor_students_count' => $request->get('settings.show_instructor_students_count') === 'yes' ? 'yes' : 'no',
101 + 'sequential_lesson_order' => $request->get('settings.sequential_lesson_order') === 'yes' ? 'yes' : 'no',
86 102 ],
87 - 'serial' => $serial
103 + 'serial' => $serial,
88 104 ];
89 105
90 106 $lockScreenType = $request->get('settings.custom_lock_screen');
91 - if (!in_array($lockScreenType, ['yes', 'no', 'redirect']) || $request->get('privacy') != 'private') {
107 + if (!in_array($lockScreenType, [ 'yes', 'no', 'redirect' ]) || $request->get('privacy') != 'private') {
92 108 $lockScreenType = 'no';
93 109 }
94 110
95 111 $courseData['settings']['custom_lock_screen'] = $lockScreenType;
@@ -97,9 +113,9 @@
97 113 if ($lockScreenType === 'redirect') {
98 114 $redirectUrl = $request->get('settings.onboard_redirect_url');
99 115 if (!$redirectUrl || !filter_var($redirectUrl, FILTER_VALIDATE_URL)) {
100 116 return $this->sendError([
101 - 'message' => __('Course Redirect URL is not valid', 'fluent-community')
117 + 'message' => __('Course Redirect URL is not valid', 'fluent-community'),
102 118 ]);
103 119 }
104 120 $courseData['settings']['onboard_redirect_url'] = sanitize_url($redirectUrl);
105 121 }
@@ -109,9 +125,9 @@
109 125 }
110 126
111 127 $slug = $request->get('slug');
112 128
113 - $slug = $slug ?: $courseData['title'];
129 + $slug = $slug ? $slug : $courseData['title'];
114 130
115 131 $slug = preg_replace('/[^a-zA-Z0-9-_]/', '', $slug);
116 132
117 133 $slug = sanitize_title($slug, '');
@@ -131,9 +147,9 @@
131 147 do_action('fluent_community/course/before_create', $courseData);
132 148
133 149 $course = Course::create($courseData);
134 150
135 - $imageTypes = ['cover_photo', 'logo'];
151 + $imageTypes = [ 'cover_photo', 'logo' ];
136 152
137 153 $metaData = [];
138 154 foreach ($imageTypes as $type) {
139 155 if (!empty($request->get($type))) {
@@ -145,9 +161,9 @@
145 161 $media->update([
146 162 'is_active' => true,
147 163 'user_id' => get_current_user_id(),
148 164 'sub_object_id' => $course->id,
149 - 'object_source' => 'space_' . $type
165 + 'object_source' => 'space_' . $type,
150 166 ]);
151 167 }
152 168 }
153 169
@@ -166,16 +182,17 @@
166 182 do_action('fluent_community/course/created', $course);
167 183
168 184 return [
169 185 'message' => __('Course has been created successfully', 'fluent-community'),
170 - 'course' => $course
186 + 'course' => $course,
171 187 ];
172 188 }
173 189
174 190 public function findCourse(Request $request, $courseId)
175 191 {
192 + /** @var Course $course */
176 193 $course = Course::where('id', $courseId)
177 - ->with(['owner'])
194 + ->with([ 'owner' ])
178 195 ->firstOrFail();
179 196
180 197 $course->students_count = $course->students()->count();
181 198 $course->course_type = $course->settings['course_type'];
@@ -192,9 +209,9 @@
192 209
193 210 $course = apply_filters('fluent_community/course_info', $course, $request->all());
194 211
195 212 return [
196 - 'course' => $course
213 + 'course' => $course,
197 214 ];
198 215 }
199 216
200 217 public function updateCourse(Request $request, $courseId)
@@ -204,9 +221,9 @@
204 221 'description' => 'required',
205 222 'privacy' => 'required|in:public,private,secret',
206 223 'status' => 'required|in:draft,published,archived',
207 224 'course_type' => 'required|in:self_paced,structured,scheduled',
208 - 'created_by' => 'exists:users,ID'
225 + 'created_by' => 'exists:users,ID',
209 226 ]);
210 227
211 228 $course = Course::findOrFail($courseId);
212 229
@@ -212,12 +229,12 @@
212 229
213 230 $courseData = [
214 231 'title' => $request->getSafe('title', 'sanitize_text_field'),
215 232 'privacy' => $request->get('privacy'),
216 - 'description' => wp_kses_post($request->get('description')),
233 + 'description' => wp_kses_post((string) $request->get('description', '')),
217 234 'status' => $request->get('status'),
218 235 'cover_photo' => $request->getSafe('cover_photo', 'sanitize_url'),
219 - 'parent_id' => $request->get('parent_id') ?: NULL,
236 + 'parent_id' => $request->get('parent_id') ?: null, // phpcs:ignore Universal.Operators.DisallowShortTernary.Found
220 237 ];
221 238
222 239 $slug = $request->get('slug');
223 240 if ($slug && $course->slug != $slug) {
@@ -228,9 +245,9 @@
228 245 ->exists();
229 246
230 247 if ($exist || !$slug) {
231 248 return $this->sendError([
232 - 'message' => __('Slug is already taken. Please use a different slug', 'fluent-community')
249 + 'message' => __('Slug is already taken. Please use a different slug', 'fluent-community'),
233 250 ]);
234 251 }
235 252
236 253 $courseData['slug'] = $slug;
@@ -239,9 +256,9 @@
239 256 if ($request->get('created_by') && Helper::isSiteAdmin()) {
240 257 $courseData['created_by'] = (int)$request->get('created_by');
241 258 }
242 259
243 - $imageTypes = ['cover_photo', 'logo'];
260 + $imageTypes = [ 'cover_photo', 'logo' ];
244 261
245 262 foreach ($imageTypes as $type) {
246 263 if (!empty($request->get($type))) {
247 264 $media = Helper::getMediaFromUrl($request->get($type));
@@ -252,9 +269,9 @@
252 269 $media->update([
253 270 'is_active' => true,
254 271 'user_id' => get_current_user_id(),
255 272 'sub_object_id' => $course->id,
256 - 'object_source' => 'space_' . $type
273 + 'object_source' => 'space_' . $type,
257 274 ]);
258 275 } else {
259 276 $courseData[$type] = null;
260 277 }
@@ -263,9 +280,9 @@
263 280 $existingSettings = $course->settings;
264 281 $existingSettings['course_type'] = $request->get('course_type');
265 282
266 283 $lockScreenType = $request->get('settings.custom_lock_screen');
267 - if (!in_array($lockScreenType, ['yes', 'no', 'redirect']) || $request->get('privacy') != 'private') {
284 + if (!in_array($lockScreenType, [ 'yes', 'no', 'redirect' ]) || $request->get('privacy') != 'private') {
268 285 $lockScreenType = 'no';
269 286 }
270 287 if ($lockScreenType == 'redirect') {
271 288 $redirectUrl = $request->get('settings.onboard_redirect_url');
@@ -270,9 +287,9 @@
270 287 if ($lockScreenType == 'redirect') {
271 288 $redirectUrl = $request->get('settings.onboard_redirect_url');
272 289 if (!$redirectUrl || !filter_var($redirectUrl, FILTER_VALIDATE_URL)) {
273 290 return $this->sendError([
274 - 'message' => __('Course Redirect URL is not valid', 'fluent-community')
291 + 'message' => __('Course Redirect URL is not valid', 'fluent-community'),
275 292 ]);
276 293 }
277 294 $existingSettings['onboard_redirect_url'] = sanitize_url($redirectUrl);
278 295 }
@@ -284,10 +301,12 @@
284 301 $existingSettings['hide_members_count'] = $request->get('settings.hide_members_count') === 'yes' ? 'yes' : 'no';
285 302 $existingSettings['hide_instructor_view'] = $request->get('settings.hide_instructor_view') === 'yes' ? 'yes' : 'no';
286 303 $existingSettings['show_instructor_students_count'] = $request->get('settings.show_instructor_students_count') === 'yes' ? 'yes' : 'no';
287 304 $existingSettings['show_paywalls'] = $request->get('settings.show_paywalls') === 'yes' ? 'yes' : 'no';
305 + $existingSettings['show_welcome_banner'] = $request->get('settings.show_welcome_banner') === 'yes' ? 'yes' : 'no';
288 306 $existingSettings['course_layout'] = $request->get('settings.course_layout') === 'modern' ? 'modern' : 'classic';
289 - $existingSettings['course_details'] = CustomSanitizer::unslashMarkdown(trim($request->get('settings.course_details')));
307 + $existingSettings['course_details'] = CustomSanitizer::unslashMarkdown(trim((string) $request->get('settings.course_details', '')));
308 + $existingSettings['sequential_lesson_order'] = $request->get('settings.sequential_lesson_order') === 'yes' ? 'yes' : 'no';
290 309
291 310 if ($request->get('privacy') == 'public' && $existingSettings['course_type'] == 'self_paced') {
292 311 $existingSettings['public_lesson_view'] = $request->get('settings.public_lesson_view') == 'yes' ? 'yes' : 'no';
293 312 } else {
@@ -326,9 +345,9 @@
326 345 }
327 346
328 347 return [
329 348 'message' => __('Course has been updated successfully.', 'fluent-community'),
330 - 'course' => $course
349 + 'course' => $course,
331 350 ];
332 351 }
333 352
334 353 public function duplicateCourse(Request $request, $courseId)
@@ -342,8 +361,9 @@
342 361 $courseData['created_by'] = get_current_user_id();
343 362
344 363 do_action('fluent_community/course/before_create', $courseData);
345 364
365 + /** @var Course $newCourse */
346 366 $newCourse = $original->replicate();
347 367 $newCourse->title = $courseData['title'];
348 368 $newCourse->slug = $courseData['slug'];
349 369 $newCourse->status = $courseData['status'];
@@ -358,18 +378,24 @@
358 378 ->where('space_id', $original->id)
359 379 ->get();
360 380
361 381 foreach ($topics as $topic) {
382 + /** @var CourseTopic $topic */
362 383 $newTopic = $topic->replicate();
363 384 $newTopic->space_id = $newCourse->id;
364 385 $newTopic->save();
365 386
387 + do_action('fluent_community/section/created', $newTopic, $newCourse);
388 +
366 389 foreach ($topic->lessons as $lesson) {
390 + /** @var CourseLesson $lesson */
367 391 $newLesson = $lesson->replicate();
368 392 $newLesson->space_id = $newCourse->id;
369 393 $newLesson->parent_id = $newTopic->id;
370 394 $newLesson->save();
371 395 CourseHelper::copyLessonDocuments($lesson, $newLesson);
396 +
397 + do_action('fluent_community/lesson/created', $newLesson, $newTopic);
372 398 }
373 399 }
374 400
375 401 do_action('fluent_community/course/created', $newCourse);
@@ -375,9 +401,9 @@
375 401 do_action('fluent_community/course/created', $newCourse);
376 402
377 403 return [
378 404 'message' => __('Course duplicated successfully.', 'fluent-community'),
379 - 'course' => $newCourse
405 + 'course' => $newCourse,
380 406 ];
381 407 }
382 408
383 409 public function deleteCourse(Request $request, $courseId)
@@ -419,33 +445,34 @@
419 445
420 446 do_action('fluent_community/course/deleted', $courseId);
421 447
422 448 return [
423 - 'message' => __('Course has been deleted successfully along with all the associated data', 'fluent-community')
449 + 'message' => __('Course has been deleted successfully along with all the associated data', 'fluent-community'),
424 450 ];
425 451 }
426 452
427 453 public function getCourseComments(Request $request, $courseId)
428 454 {
429 - Course::findOrFail($courseId);
455 + $course = Course::findOrFail($courseId);
430 456
431 - $comments = Comment::whereHas('post', function ($q) use ($courseId) {
432 - return $q->where('space_id', $courseId);
433 - })
457 + $comments = Comment::byContentModerationAccessStatus($this->getUser(), $course)
458 + ->whereHas('post', function ($q) use ($courseId) {
459 + return $q->where('space_id', $courseId);
460 + })
434 461 ->orderBy('id', 'DESC')
435 462 ->with([
436 463 'post' => function ($q) {
437 - return $q->select(['id', 'title', 'slug']);
464 + return $q->select([ 'id', 'title', 'slug' ]);
438 465 },
439 466 'xprofile' => function ($q) {
440 467 $q->select(ProfileHelper::getXProfilePublicFields());
441 - }
468 + },
442 469 ])
443 470 ->paginate();
444 471
445 472 foreach ($comments as $comment) {
446 473 if ($comment->user) {
447 - $comment->user->makeHidden(['user_email']);
474 + $comment->user->makeHidden([ 'user_email' ]);
448 475 }
449 476 $likedIds = FeedsHelper::getLikedIdsByUserFeedId($comment->post_id, get_current_user_id());
450 477 if ($likedIds && in_array($comment->id, $likedIds)) {
451 478 $comment->liked = 1;
@@ -452,9 +479,9 @@
452 479 }
453 480 }
454 481
455 482 $data = [
456 - 'comments' => $comments
483 + 'comments' => $comments,
457 484 ];
458 485
459 486 return apply_filters('fluent_community/admin_course_comments_api_response', $data, $request->all());
460 487 }
@@ -464,13 +491,27 @@
464 491 Course::findOrFail($courseId);
465 492
466 493 $search = $request->getSafe('search');
467 494
468 - $students = XProfile::whereHas('space_pivot', function ($q) use ($courseId) {
469 - return $q->where('space_id', $courseId)
470 - ->where('role', 'student');
471 - })
472 - ->searchBy($search)
495 + $defaultDirections = [
496 + 'display_name' => 'ASC',
497 + 'created_at' => 'DESC',
498 + 'last_activity' => 'DESC',
499 + ];
500 +
501 + $sortBy = $request->getSafe('sort_by', 'sanitize_text_field', 'created_at');
502 + $sortColumn = in_array($sortBy, array_keys($defaultDirections), true) ? $sortBy : 'created_at';
503 + $sortDir = strtoupper($request->getSafe('sort_dir', 'sanitize_text_field', ''));
504 + $sortDirection = in_array($sortDir, ['ASC', 'DESC'], true) ? $sortDir : $defaultDirections[$sortColumn];
505 + $orderColumn = $sortColumn === 'created_at'
506 + ? 'fcom_space_user.created_at'
507 + : 'fcom_xprofile.' . $sortColumn;
508 +
509 + $publicFields = array_map(function ($field) {
510 + return 'fcom_xprofile.' . $field;
511 + }, ProfileHelper::getXProfilePublicFields());
512 +
513 + $students = XProfile::searchBy($search)
473 514 ->whereHas('user')
474 515 ->with([
475 516 'space_pivot' => function ($q) use ($courseId) {
476 517 return $q->where('space_id', $courseId);
@@ -475,9 +516,15 @@
475 516 'space_pivot' => function ($q) use ($courseId) {
476 517 return $q->where('space_id', $courseId);
477 518 },
478 519 ])
479 - ->select(ProfileHelper::getXProfilePublicFields())
520 + ->join('fcom_space_user', function ($join) use ($courseId) {
521 + $join->on('fcom_space_user.user_id', '=', 'fcom_xprofile.user_id')
522 + ->where('fcom_space_user.space_id', $courseId)
523 + ->where('fcom_space_user.role', 'student');
524 + })
525 + ->select($publicFields)
526 + ->orderBy($orderColumn, $sortDirection)
480 527 ->paginate();
481 528
482 529 $studentUserIds = $students->pluck('user_id')->toArray();
483 530
@@ -487,9 +534,9 @@
487 534 $student->progress = $progressMap[$student->user_id] ?? 0;
488 535 }
489 536
490 537 $data = [
491 - 'students' => $students
538 + 'students' => $students,
492 539 ];
493 540
494 541 return apply_filters('fluent_community/admin_course_students_api_response', $data, $request->all());
495 542 }
@@ -498,9 +545,9 @@
498 545 {
499 546 $course = Course::findOrFail($courseId);
500 547
501 548 $this->validate($request->all(), [
502 - 'user_id' => 'required|exists:users,ID'
549 + 'user_id' => 'required|exists:users,ID',
503 550 ]);
504 551
505 552 $userId = (int)$request->get('user_id');
506 553 $targetUser = User::findOrFail($userId);
@@ -507,9 +554,9 @@
507 554 $xprofile = $targetUser->syncXProfile();
508 555
509 556 if ($xprofile && $xprofile->status != 'active') {
510 557 return $this->sendError([
511 - 'message' => __('Selected user is not active', 'fluent-community')
558 + 'message' => __('Selected user is not active', 'fluent-community'),
512 559 ]);
513 560 }
514 561
515 562 $enrolled = CourseHelper::enrollCourse($course, $userId, 'by_admin');
@@ -515,14 +562,14 @@
515 562 $enrolled = CourseHelper::enrollCourse($course, $userId, 'by_admin');
516 563
517 564 if (!$enrolled) {
518 565 return $this->sendError([
519 - 'message' => __('User is already added to this course.', 'fluent-community')
566 + 'message' => __('User is already added to this course.', 'fluent-community'),
520 567 ]);
521 568 }
522 569
523 570 return [
524 - 'message' => __('User has been added to this course', 'fluent-community')
571 + 'message' => __('User has been added to this course', 'fluent-community'),
525 572 ];
526 573 }
527 574
528 575 public function removeStudent(Request $request, $courseId, $studentId)
@@ -534,9 +581,9 @@
534 581 ->first();
535 582
536 583 if (!$student) {
537 584 return $this->sendError([
538 - 'message' => __('Selected user is not a student of this course', 'fluent-community')
585 + 'message' => __('Selected user is not a student of this course', 'fluent-community'),
539 586 ]);
540 587 }
541 588
542 589 Helper::removeFromSpace($course, $studentId, 'by_admin');
@@ -541,15 +588,38 @@
541 588
542 589 Helper::removeFromSpace($course, $studentId, 'by_admin');
543 590
544 591 return [
545 - 'message' => __('Student has been removed from this course', 'fluent-community')
592 + 'message' => __('Student has been removed from this course', 'fluent-community'),
546 593 ];
547 594 }
548 595
596 + public function resetStudentProgress(Request $request, $courseId, $studentId)
597 + {
598 + Course::findOrFail($courseId);
599 +
600 + $pivot = SpaceUserPivot::where('space_id', $courseId)
601 + ->where('user_id', $studentId)
602 + ->where('role', 'student')
603 + ->first();
604 +
605 + if (!$pivot) {
606 + return $this->sendError([
607 + 'message' => __('This student is not enrolled in this course.', 'fluent-community'),
608 + ]);
609 + }
610 +
611 + CourseHelper::resetCourseProgress($courseId, (int) $studentId);
612 +
613 + return [
614 + 'message' => __("Student's progress has been reset.", 'fluent-community'),
615 + ];
616 + }
617 +
549 618 public function getSections(Request $request, $courseId)
550 619 {
551 620 $course = Course::findOrFail($courseId);
621 + /** @var Course $course */
552 622
553 623 $sectionsQuery = CourseTopic::where('space_id', $courseId)
554 624 ->orderBy('priority', 'ASC')
555 625 ->orderBy('id', 'ASC');
@@ -555,21 +625,23 @@
555 625 ->orderBy('id', 'ASC');
556 626
557 627 if (in_array('only_published', $request->get('conditions', []))) {
558 628 $sectionsQuery->where('status', 'published')
559 - ->with(['lessons' => function ($q) {
629 + ->with([
630 + 'lessons' => function ($q) {
560 631 $q->where('status', 'published');
561 - }]);
632 + },
633 + ]);
562 634 }
563 635
564 636 if (empty($request->get('conditions', []))) {
565 - $sectionsQuery->with(['lessons']);
637 + $sectionsQuery->with([ 'lessons' ]);
566 638 }
567 639
568 640 $sections = $sectionsQuery->get();
569 641
570 642 $data = [
571 - 'sections' => $sections
643 + 'sections' => $sections,
572 644 ];
573 645
574 646 if ($request->get('with_lock_screen')) {
575 647 $data['lockscreen'] = LockscreenService::getLockscreenSettings($course);
@@ -584,13 +656,13 @@
584 656 ->whereHas('course', function ($query) use ($courseId) {
585 657 $query->where('id', $courseId);
586 658 })
587 659 ->where('id', $topicId)
588 - ->with(['lessons'])
660 + ->with([ 'lessons' ])
589 661 ->firstOrFail();
590 662
591 663 $data = [
592 - 'topic' => $topic
664 + 'topic' => $topic,
593 665 ];
594 666
595 667 return apply_filters('fluent_community/admin_course_section_api_response', $data, $request->all());
596 668 }
@@ -603,9 +675,10 @@
603 675 ->whereIn('id', array_keys($indexes))
604 676 ->get();
605 677
606 678 foreach ($sections as $section) {
607 - if (!isset($indexes[$section->id])) continue;
679 + if (!isset($indexes[$section->id])) { continue;
680 + }
608 681 $section->priority = $indexes[$section->id];
609 682 $section->save();
610 683 }
611 684
@@ -610,9 +683,9 @@
610 683 }
611 684
612 685 return [
613 686 'sections' => $sections,
614 - 'message' => __('Section indexes have been updated successfully.', 'fluent-community')
687 + 'message' => __('Section indexes have been updated successfully.', 'fluent-community'),
615 688 ];
616 689 }
617 690
618 691 public function resetLessonIndexes(Request $request, $courseId, $sectionId)
@@ -624,9 +697,10 @@
624 697 ->whereIn('id', array_keys($indexes))
625 698 ->get();
626 699
627 700 foreach ($lessons as $lesson) {
628 - if (!isset($indexes[$lesson->id])) continue;
701 + if (!isset($indexes[$lesson->id])) { continue;
702 + }
629 703 $lesson->priority = $indexes[$lesson->id];
630 704 $lesson->save();
631 705 }
632 706
@@ -631,9 +705,9 @@
631 705 }
632 706
633 707 return [
634 708 'lessons' => $lessons,
635 - 'message' => __('Lesson indexes have been updated successfully.', 'fluent-community')
709 + 'message' => __('Lesson indexes have been updated successfully.', 'fluent-community'),
636 710 ];
637 711 }
638 712
639 713 public function moveLesson(Request $request, $courseId)
@@ -641,18 +715,17 @@
641 715 $lessonId = $request->getSafe('lesson_id', 'intval');
642 716 $sectionId = $request->getSafe('section_id', 'intval');
643 717
644 718 Course::findOrFail($courseId);
645 - CourseTopic::findOrFail($sectionId);
719 + $section = CourseTopic::where('space_id', $courseId)->findOrFail($sectionId);
720 + $lesson = CourseLesson::where('space_id', $courseId)->findOrFail($lessonId);
646 721
647 - $lesson = CourseLesson::findOrFail($lessonId);
648 -
649 722 $lesson->update([
650 - 'parent_id' => $sectionId
723 + 'parent_id' => $section->id,
651 724 ]);
652 725
653 726 return [
654 - 'message' => __('Lesson has been moved successfully', 'fluent-community')
727 + 'message' => __('Lesson has been moved successfully', 'fluent-community'),
655 728 ];
656 729 }
657 730
658 731 public function createSection(Request $request, $courseId)
@@ -657,30 +730,32 @@
657 730
658 731 public function createSection(Request $request, $courseId)
659 732 {
660 733 $this->validate($request->all(), [
661 - 'title' => 'required'
734 + 'title' => 'required',
662 735 ]);
663 736
664 737 $sectionData = [
665 738 'title' => $request->getSafe('title'),
666 739 'space_id' => $courseId,
667 - 'status' => 'published'
740 + 'status' => 'published',
668 741 ];
669 742
670 - Course::findOrFail($courseId);
743 + $course = Course::findOrFail($courseId);
671 744
672 745 $latestPriority = CourseTopic::where('type', 'course_section')->where('space_id', $courseId)->max('priority');
673 746
674 - $sectionData['priority'] = $latestPriority ? $latestPriority + 1 : 0;
747 + $sectionData['priority'] = (int) $latestPriority + 1;
675 748
676 749 $section = CourseTopic::create($sectionData);
677 750
678 751 $section->load('lessons');
679 752
753 + do_action('fluent_community/section/created', $section, $course);
754 +
680 755 return [
681 756 'message' => __('Section has been created successfully.', 'fluent-community'),
682 - 'section' => $section
757 + 'section' => $section,
683 758 ];
684 759 }
685 760
686 761 public function updateSection(Request $request, $courseId, $tipicId)
@@ -686,9 +761,9 @@
686 761 public function updateSection(Request $request, $courseId, $tipicId)
687 762 {
688 763 $this->validate($request->all(), [
689 764 'title' => 'required',
690 - 'status' => 'required|in:draft,published,archived'
765 + 'status' => 'required|in:draft,published,archived',
691 766 ]);
692 767
693 768 Course::findOrFail($courseId);
694 769
@@ -698,9 +773,9 @@
698 773
699 774
700 775 $topicData = [
701 776 'title' => $request->getSafe('title'),
702 - 'status' => $request->get('status')
777 + 'status' => $request->get('status'),
703 778 ];
704 779
705 780 $topic->update($topicData);
706 781
@@ -705,9 +780,9 @@
705 780 $topic->update($topicData);
706 781
707 782 return [
708 783 'message' => __('Topic has been updated successfully.', 'fluent-community'),
709 - 'topic' => $topic
784 + 'topic' => $topic,
710 785 ];
711 786 }
712 787
713 788 public function patchSection(Request $request, $courseId, $tipicId)
@@ -717,21 +792,32 @@
717 792 $topic = CourseTopic::where('space_id', $courseId)
718 793 ->where('id', $tipicId)
719 794 ->firstOrFail();
720 795
721 - $acceptedFields = ['title', 'status'];
796 + $acceptedFields = [ 'title', 'status' ];
722 797
723 798 if ($course->getCourseType() == 'scheduled') {
724 799 $acceptedFields[] = 'scheduled_at';
725 - } else if ($course->getCourseType() == 'structured') {
800 + } elseif ($course->getCourseType() == 'structured') {
726 801 $acceptedFields[] = 'reactions_count';
727 802 }
728 803
804 + // Request::only() does not sanitize. Section titles currently render as escaped
805 + // text, so this is hygiene rather than a live sink, but keep the stored value
806 + // clean so a future v-html render cannot turn it into one.
729 807 $topicData = $request->only($acceptedFields);
730 808
809 + if (isset($topicData['title'])) {
810 + $topicData['title'] = sanitize_text_field($topicData['title']);
811 + }
812 +
813 + if (isset($topicData['status'])) {
814 + $topicData['status'] = sanitize_text_field($topicData['status']);
815 + }
816 +
731 817 if (!empty($topicData['scheduled_at'])) {
732 818 $topic->reactions_count = 0;
733 - } else if (isset($topicData['reactions_count'])) {
819 + } elseif (isset($topicData['reactions_count'])) {
734 820 $topic->scheduled_at = null;
735 821 $topic->reactions_count = $topicData['reactions_count'];
736 822 }
737 823
@@ -756,9 +842,9 @@
756 842 }
757 843
758 844 return [
759 845 'message' => __('Topic has been updated successfully.', 'fluent-community'),
760 - 'topic' => $topic
846 + 'topic' => $topic,
761 847 ];
762 848 }
763 849
764 850 public function copySection(Request $request, $toCourseId)
@@ -769,12 +855,13 @@
769 855 $fromCourse = Course::findOrFail($fromCourseId);
770 856
771 857 if (!$fromCourse->isCourseAdmin()) {
772 858 return $this->sendError([
773 - 'message' => __('You do not have permission to access this course', 'fluent-community')
859 + 'message' => __('You do not have permission to access this course', 'fluent-community'),
774 860 ]);
775 861 }
776 862
863 + /** @var CourseTopic $originalSection */
777 864 $originalSection = CourseTopic::where('id', $sectionId)
778 865 ->where('space_id', $fromCourseId)
779 866 ->firstOrFail();
780 867
@@ -781,20 +868,26 @@
781 868 $toCourse = Course::findOrFail($toCourseId);
782 869
783 870 $latestPriority = CourseTopic::where('type', 'course_section')->where('space_id', $toCourse->id)->max('priority');
784 871
872 + /** @var CourseTopic $newSection */
785 873 $newSection = $originalSection->replicate();
786 874 $newSection->space_id = $toCourse->id;
787 - $newSection->priority = $latestPriority ? $latestPriority + 1 : 0;
875 + $newSection->priority = (int) $latestPriority + 1;
788 876 $newSection->save();
789 877
878 + do_action('fluent_community/section/created', $newSection, $toCourse);
879 +
790 880 $originalLessons = CourseLesson::where('parent_id', $originalSection->id)->get();
791 881 foreach ($originalLessons as $lesson) {
882 + /** @var CourseLesson $lesson */
792 883 $newLesson = $lesson->replicate();
793 884 $newLesson->space_id = $toCourse->id;
794 885 $newLesson->parent_id = $newSection->id;
795 886 $newLesson->save();
796 887 CourseHelper::copyLessonDocuments($lesson, $newLesson);
888 +
889 + do_action('fluent_community/lesson/created', $newLesson, $newSection);
797 890 }
798 891
799 892 $newSection->load('lessons');
800 893
@@ -799,9 +892,9 @@
799 892 $newSection->load('lessons');
800 893
801 894 return [
802 895 'message' => __('Section has been copied to the selected course', 'fluent-community'),
803 - 'section' => $newSection
896 + 'section' => $newSection,
804 897 ];
805 898 }
806 899
807 900 public function deleteSection(Request $request, $courseId, $sectionId)
@@ -807,9 +900,9 @@
807 900 public function deleteSection(Request $request, $courseId, $sectionId)
808 901 {
809 902 $topic = CourseTopic::where([
810 903 'id' => $sectionId,
811 - 'space_id' => $courseId
904 + 'space_id' => $courseId,
812 905 ])->firstOrFail();
813 906
814 907 do_action('fluent_community/section/before_deleted', $topic);
815 908
@@ -816,9 +909,9 @@
816 909 $topic->delete();
817 910
818 911 $lessons = CourseLesson::where([
819 912 'parent_id' => $sectionId,
820 - 'space_id' => $courseId
913 + 'space_id' => $courseId,
821 914 ])->get();
822 915
823 916 foreach ($lessons as $lesson) {
824 917 do_action('fluent_community/lesson/before_deleted', $lesson);
@@ -825,9 +918,9 @@
825 918 $lesson->delete();
826 919 }
827 920
828 921 return [
829 - 'message' => __('Section has been deleted successfully.', 'fluent-community')
922 + 'message' => __('Section has been deleted successfully.', 'fluent-community'),
830 923 ];
831 924 }
832 925
833 926 public function getLessons(Request $request, $courseId)
@@ -834,9 +927,10 @@
834 927 {
835 928 Course::findOrFail($courseId);
836 929
837 930 $lessons = CourseLesson::where('space_id', $courseId)
838 - ->orderBy('priority', 'ASC');
931 + ->orderBy('priority', 'ASC')
932 + ->orderBy('id', 'ASC');
839 933
840 934 $topicId = (int)$request->get('topic_id');
841 935
842 936 if ($topicId) {
@@ -845,9 +939,9 @@
845 939
846 940 $lessons = $lessons->get();
847 941
848 942 $data = [
849 - 'lessons' => $lessons
943 + 'lessons' => $lessons,
850 944 ];
851 945
852 946 return apply_filters('fluent_community/admin_course_lessons_api_response', $data, $request->all());
853 947 }
@@ -857,23 +951,26 @@
857 951 $lesson = CourseLesson::whereHas('course', function ($query) use ($courseId) {
858 952 $query->where('id', $courseId);
859 953 })
860 954 ->where('id', $lessonId)
861 - ->with(['topic', 'course'])
955 + ->with([ 'topic', 'course' ])
862 956 ->firstOrFail();
863 957
864 958 $data = [
865 - 'lesson' => $lesson
959 + 'lesson' => $lesson,
866 960 ];
867 961
868 962 return apply_filters('fluent_community/admin_course_lesson_api_response', $data, $request->all());
869 963 }
870 964
965 + /**
966 + * Expects `title` and `section_id` at the top level of the request.
967 + */
871 968 public function createLesson(Request $request, $courseId)
872 969 {
873 970 $this->validate($request->all(), [
874 971 'title' => 'required',
875 - 'section_id' => 'required'
972 + 'section_id' => 'required',
876 973 ]);
877 974
878 975 $sectionId = (int)$request->get('section_id');
879 976
@@ -886,9 +983,9 @@
886 983 $lessonData = [
887 984 'title' => $request->getSafe('title'),
888 985 'parent_id' => $topic->id,
889 986 'space_id' => $courseId,
890 - 'status' => 'draft'
987 + 'status' => 'draft',
891 988 ];
892 989
893 990 $latestPriority = CourseLesson::where('type', 'course_lesson')
894 991 ->where('parent_id', $sectionId)
@@ -894,9 +991,9 @@
894 991 ->where('parent_id', $sectionId)
895 992 ->where('space_id', $courseId)
896 993 ->max('priority');
897 994
898 - $lessonData['priority'] = $latestPriority ? $latestPriority + 1 : 0;
995 + $lessonData['priority'] = (int) $latestPriority + 1;
899 996
900 997 $lessonData = apply_filters('fluent_community/lesson/create_data', $lessonData, $request);
901 998
902 999 $lesson = CourseLesson::create($lessonData);
@@ -902,24 +999,29 @@
902 999 $lesson = CourseLesson::create($lessonData);
903 1000
904 1001 $lesson = CourseLesson::findOrFail($lesson->id);
905 1002
1003 + do_action('fluent_community/lesson/created', $lesson, $topic);
1004 +
906 1005 return [
907 1006 'message' => __('Lesson has been created successfully.', 'fluent-community'),
908 - 'lesson' => $lesson
1007 + 'lesson' => $lesson,
909 1008 ];
910 1009 }
911 1010
1011 + /**
1012 + * Expects the fields nested under `lesson`, referencing the section as `parent_id`.
1013 + */
912 1014 public function updateLesson(Request $request, $courseId, $lessionId)
913 1015 {
914 1016 Course::findOrFail($courseId);
915 1017
916 - $lessonData = $request->get('lesson');
1018 + $lessonData = (array)$request->get('lesson');
917 1019
918 1020 $this->validate($lessonData, [
919 1021 'title' => 'required',
920 1022 'parent_id' => 'required',
921 - 'status' => 'required|in:draft,published,archived'
1023 + 'status' => 'required|in:draft,published,archived',
922 1024 ]);
923 1025
924 1026 CourseTopic::whereHas('course', function ($query) use ($courseId) {
925 1027 $query->where('id', $courseId);
@@ -926,8 +1028,9 @@
926 1028 })
927 1029 ->where('id', $lessonData['parent_id'])
928 1030 ->firstOrFail();
929 1031
1032 + /** @var CourseLesson $lesson */
930 1033 $lesson = CourseLesson::where('id', $lessionId)
931 1034 ->where('space_id', $courseId)
932 1035 ->firstOrFail();
933 1036
@@ -935,12 +1038,12 @@
935 1038
936 1039 $updatedMeta = CourseHelper::sanitizeLessonMeta(Arr::get($lessonData, 'meta', []), $lesson);
937 1040 $updatedMeta['document_ids'] = Arr::get($lesson->meta, 'document_ids', []);
938 1041
939 - if ($mediaId = Arr::get($updatedMeta, 'featured_image_id')) {
1042 + if ($mediaId = Arr::get($updatedMeta, 'featured_image_id')) { // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
940 1043 if (!$lesson->isQuizType()) {
941 1044 $media = wp_get_attachment_image_url($mediaId);
942 - $lesson->featured_image = $media ?: null;
1045 + $lesson->featured_image = $media ? $media : null;
943 1046 } else {
944 1047 $media = Helper::getMediaFromUrl(sanitize_url($mediaId));
945 1048 if ($media && !$media->is_active) {
946 1049 Helper::removeMediaByUrl($lesson->featured_image, $lesson->id);
@@ -948,9 +1051,9 @@
948 1051 $media->update([
949 1052 'is_active' => true,
950 1053 'user_id' => get_current_user_id(),
951 1054 'sub_object_id' => $lesson->id,
952 - 'object_source' => 'quiz_thumbnail_' . $lesson->id
1055 + 'object_source' => 'quiz_thumbnail_' . $lesson->id,
953 1056 ]);
954 1057 }
955 1058 }
956 1059 } else {
@@ -958,14 +1061,18 @@
958 1061 $lesson->featured_image = null;
959 1062 }
960 1063
961 1064 $updateData = array_filter([
962 - 'title' => sanitize_text_field(Arr::get($lessonData, 'title')),
963 - 'message' => CourseHelper::santizeLessonBody(Arr::get($lessonData, 'message')),
964 - 'status' => Arr::get($lessonData, 'status'),
965 - 'meta' => wp_parse_args($updatedMeta, $lesson->meta)
1065 + 'title' => sanitize_text_field(Arr::get($lessonData, 'title')),
1066 + 'status' => Arr::get($lessonData, 'status'),
1067 + 'meta' => wp_parse_args($updatedMeta, $lesson->meta),
966 1068 ]);
967 1069
1070 + // message bypasses array_filter so an emptied lesson body still saves
1071 + if (array_key_exists('message', $lessonData)) {
1072 + $updateData['message'] = CourseHelper::santizeLessonBody((string) Arr::get($lessonData, 'message'));
1073 + }
1074 +
968 1075 $updateData = apply_filters('fluent_community/lesson/update_data', $updateData, $lesson);
969 1076
970 1077 $lesson->fill($updateData);
971 1078 $dirtyFields = $lesson->getDirty();
@@ -973,8 +1080,12 @@
973 1080 if ($dirtyFields) {
974 1081 $lesson->save();
975 1082 $isNewlyPublished = $lesson->status === 'published' && $previousStatus !== 'published';
976 1083 do_action('fluent_community/lesson/updated', $lesson, $dirtyFields, $isNewlyPublished);
1084 +
1085 + if ($isNewlyPublished) {
1086 + do_action('fluent_community/lesson/published', $lesson);
1087 + }
977 1088 }
978 1089
979 1090 do_action('fluent_community/lesson/additional_media_updated', $request->all(), $lesson, $updateData);
980 1091
@@ -979,9 +1090,9 @@
979 1090 do_action('fluent_community/lesson/additional_media_updated', $request->all(), $lesson, $updateData);
980 1091
981 1092 return [
982 1093 'message' => __('Lesson has been updated successfully.', 'fluent-community'),
983 - 'lesson' => $lesson
1094 + 'lesson' => $lesson,
984 1095 ];
985 1096 }
986 1097
987 1098 public function patchLesson(Request $request, $courseId, $lessionId)
@@ -991,12 +1102,39 @@
991 1102 })
992 1103 ->where('id', $lessionId)
993 1104 ->firstOrFail();
994 1105
995 - $acceptedFields = ['title', 'status', 'slug'];
1106 + $previousStatus = $lesson->status;
996 1107
997 - $lessonData = array_filter($request->only($acceptedFields));
1108 + $acceptedFields = [ 'title', 'status', 'slug' ];
998 1109
1110 + // empty title/slug/status must not overwrite, but a literal "0" is a valid value
1111 + // Request::only() is a plain array pick with no sanitization, so each field is
1112 + // sanitized here the same way createLesson/updateLesson do it. The title is
1113 + // rendered with v-html in the course views, so it must not carry markup.
1114 + $lessonData = array_filter($request->only($acceptedFields), function ($value) {
1115 + return $value !== null && $value !== '';
1116 + });
1117 +
1118 + if (isset($lessonData['title'])) {
1119 + $lessonData['title'] = sanitize_text_field($lessonData['title']);
1120 + }
1121 +
1122 + if (isset($lessonData['slug'])) {
1123 + // sanitize_title() alone let an author set a slug already taken by a
1124 + // sibling lesson, which getLessonBySlug() then resolves arbitrarily.
1125 + $lessonData['slug'] = CourseLesson::uniqueSlug(
1126 + $lessonData['slug'],
1127 + $lesson->space_id,
1128 + $lesson->id,
1129 + Arr::get($lessonData, 'title', $lesson->title)
1130 + );
1131 + }
1132 +
1133 + if (isset($lessonData['status']) && !in_array($lessonData['status'], ['draft', 'published', 'archived'], true)) {
1134 + unset($lessonData['status']);
1135 + }
1136 +
999 1137 if (Arr::get($lessonData, 'status') === 'published' && $lesson->status !== 'published') {
1000 1138 if (empty($lesson->scheduled_at)) {
1001 1139 $lessonData['scheduled_at'] = current_time('mysql');
1002 1140 }
@@ -1001,18 +1139,32 @@
1001 1139 $lessonData['scheduled_at'] = current_time('mysql');
1002 1140 }
1003 1141 }
1004 1142
1143 + // message bypasses the empty-value filter above so an emptied lesson body still saves
1144 + if ($request->exists('message')) {
1145 + $lessonData['message'] = CourseHelper::santizeLessonBody((string) $request->get('message'));
1146 + }
1147 +
1005 1148 if (!empty($lessonData)) {
1006 1149 $lesson->fill($lessonData);
1007 - if ($lesson->isDirty()) {
1150 + $dirtyFields = $lesson->getDirty();
1151 +
1152 + if ($dirtyFields) {
1008 1153 $lesson->save();
1154 +
1155 + $isNewlyPublished = $lesson->status === 'published' && $previousStatus !== 'published';
1156 + do_action('fluent_community/lesson/updated', $lesson, $dirtyFields, $isNewlyPublished);
1157 +
1158 + if ($isNewlyPublished) {
1159 + do_action('fluent_community/lesson/published', $lesson);
1160 + }
1009 1161 }
1010 1162 }
1011 1163
1012 1164 return [
1013 1165 'message' => __('Lesson has been updated successfully.', 'fluent-community'),
1014 - 'lesson' => $lesson
1166 + 'lesson' => $lesson,
1015 1167 ];
1016 1168 }
1017 1169
1018 1170 public function deleteLesson(Request $request, $courseId, $lessionId)
@@ -1027,17 +1179,76 @@
1027 1179
1028 1180 $lesson->delete();
1029 1181
1030 1182 return [
1031 - 'message' => __('Lesson has been deleted successfully.', 'fluent-community')
1183 + 'message' => __('Lesson has been deleted successfully.', 'fluent-community'),
1032 1184 ];
1033 1185 }
1034 1186
1187 + public function duplicateLesson(Request $request, $courseId, $lessonId)
1188 + {
1189 + Course::findOrFail($courseId);
1190 +
1191 + $lesson = CourseLesson::where('id', $lessonId)
1192 + ->where('space_id', $courseId)
1193 + ->firstOrFail();
1194 +
1195 + $siblings = CourseLesson::where('parent_id', $lesson->parent_id)
1196 + ->where('space_id', $courseId)
1197 + ->orderBy('priority', 'ASC')
1198 + ->orderBy('id', 'ASC')
1199 + ->get();
1200 +
1201 + $sourceIndex = $siblings->search(function ($sibling) use ($lesson) {
1202 + return (int)$sibling->id === (int)$lesson->id;
1203 + });
1204 +
1205 + if ($sourceIndex === false) {
1206 + $sourceIndex = $siblings->count() - 1;
1207 + }
1208 +
1209 + $existingTitles = $siblings->pluck('title')->toArray();
1210 + $duplicateTitle = $lesson->title . ' (Copy)';
1211 + if (in_array($duplicateTitle, $existingTitles, true)) {
1212 + $counter = 2;
1213 + while (in_array($lesson->title . ' (Copy ' . $counter . ')', $existingTitles, true)) {
1214 + ++$counter;
1215 + }
1216 + $duplicateTitle = $lesson->title . ' (Copy ' . $counter . ')';
1217 + }
1218 +
1219 + /** @var CourseLesson $newLesson */
1220 + $newLesson = $lesson->replicate();
1221 + $newLesson->title = $duplicateTitle;
1222 + $newLesson->slug = null;
1223 + $newLesson->priority = $sourceIndex + 1;
1224 + $newLesson->save();
1225 +
1226 + $orderedIds = $siblings->pluck('id')->toArray();
1227 + array_splice($orderedIds, $sourceIndex + 1, 0, [ $newLesson->id ]);
1228 +
1229 + foreach ($orderedIds as $index => $siblingId) {
1230 + CourseLesson::where('id', $siblingId)->update([ 'priority' => $index ]);
1231 + }
1232 +
1233 + $newLesson = CourseLesson::findOrFail($newLesson->id);
1234 +
1235 + CourseHelper::copyLessonDocuments($lesson, $newLesson);
1236 +
1237 + do_action('fluent_community/lesson/created', $newLesson, $newLesson->topic);
1238 + do_action('fluent_community/lesson/duplicated', $newLesson, $lesson);
1239 +
1240 + return [
1241 + 'message' => __('Lesson has been duplicated successfully.', 'fluent-community'),
1242 + 'lesson' => $newLesson,
1243 + ];
1244 + }
1245 +
1035 1246 public function getOtherUsers(Request $request, $courseId)
1036 1247 {
1037 1248 $selects = [
1038 1249 'ID',
1039 - 'display_name'
1250 + 'display_name',
1040 1251 ];
1041 1252
1042 1253 if (current_user_can('list_users')) {
1043 1254 $selects[] = 'user_email';
@@ -1042,9 +1253,9 @@
1042 1253 if (current_user_can('list_users')) {
1043 1254 $selects[] = 'user_email';
1044 1255 }
1045 1256
1046 - $userQuery = User::select(['ID'])
1257 + $userQuery = User::select([ 'ID' ])
1047 1258 ->whereDoesntHave('space_pivot', function ($q) use ($courseId) {
1048 1259 $q->where('space_id', $courseId);
1049 1260 })
1050 1261 ->limit(100)
@@ -1053,9 +1264,9 @@
1053 1264 if (is_multisite()) {
1054 1265 global $wpdb;
1055 1266 $blogId = get_current_blog_id();
1056 1267 $blogPrefix = $wpdb->get_blog_prefix($blogId);
1057 - $userQuery->whereHas('usermeta', function($q) use ($blogPrefix) {
1268 + $userQuery->whereHas('usermeta', function ($q) use ($blogPrefix) {
1058 1269 $q->where('meta_key', $blogPrefix . 'capabilities');
1059 1270 });
1060 1271 }
1061 1272
@@ -1067,9 +1278,9 @@
1067 1278 ->whereIn('ID', $userIds)
1068 1279 ->paginate(100);
1069 1280
1070 1281 $data = [
1071 - 'users' => $users
1282 + 'users' => $users,
1072 1283 ];
1073 1284
1074 1285 return apply_filters('fluent_community/admin_course_non_members_api_response', $data, $request->all());
1075 1286 }
@@ -1089,9 +1300,9 @@
1089 1300 $course->save();
1090 1301
1091 1302 return [
1092 1303 'message' => __('Links have been updated for the course', 'fluent-community'),
1093 - 'links' => $links
1304 + 'links' => $links,
1094 1305 ];
1095 1306 }
1096 1307
1097 1308 public function getMetaSettings(Request $request, $id)
@@ -1100,14 +1311,14 @@
1100 1311 $metaSettings = apply_filters('fluent_community/course/meta_fields', [], $course, $request->all());
1101 1312
1102 1313 if (!$metaSettings) {
1103 1314 return [
1104 - 'meta_settings' => null
1315 + 'meta_settings' => null,
1105 1316 ];
1106 1317 }
1107 1318
1108 1319 return [
1109 - 'meta_settings' => $metaSettings
1320 + 'meta_settings' => $metaSettings,
1110 1321 ];
1111 1322 }
1112 1323
1113 1324 public function getOtherInstructors(Request $request, $courseId)
@@ -1115,15 +1326,24 @@
1115 1326 $search = $request->getSafe('search');
1116 1327
1117 1328 Course::findOrFail($courseId);
1118 1329
1119 - $instructors = User::select(['ID', 'display_name', 'user_email'])
1330 + $selects = [
1331 + 'ID',
1332 + 'display_name',
1333 + ];
1334 +
1335 + if (current_user_can('list_users')) {
1336 + $selects[] = 'user_email';
1337 + }
1338 +
1339 + $instructors = User::select($selects)
1120 1340 ->limit(100)
1121 1341 ->searchBy($search)
1122 1342 ->get();
1123 1343
1124 1344 $data = [
1125 - 'instructors' => $instructors
1345 + 'instructors' => $instructors,
1126 1346 ];
1127 1347
1128 1348 return apply_filters('fluent_community/admin_course_other_instructors_api_response', $data, $request->all());
1129 1349 }