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