PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.01
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
← All changes | Modules/Course/Http/Controllers/CourseAdminController.php +263 -118 2.6.02.10.01 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,38 @@
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 + CourseHelper::resetCourseProgress($courseId, (int) $studentId);
612 +
613 + return [
614 + 'message' => __("Student's progress has been reset.", 'fluent-community'),
615 + ];
616 + }
617 +
567 618 public function getSections(Request $request, $courseId)
568 619 {
569 620 $course = Course::findOrFail($courseId);
621 + /** @var Course $course */
570 622
571 623 $sectionsQuery = CourseTopic::where('space_id', $courseId)
572 624 ->orderBy('priority', 'ASC')
573 625 ->orderBy('id', 'ASC');
@@ -573,21 +625,23 @@
573 625 ->orderBy('id', 'ASC');
574 626
575 627 if (in_array('only_published', $request->get('conditions', []))) {
576 628 $sectionsQuery->where('status', 'published')
577 - ->with(['lessons' => function ($q) {
629 + ->with([
630 + 'lessons' => function ($q) {
578 631 $q->where('status', 'published');
579 - }]);
632 + },
633 + ]);
580 634 }
581 635
582 636 if (empty($request->get('conditions', []))) {
583 - $sectionsQuery->with(['lessons']);
637 + $sectionsQuery->with([ 'lessons' ]);
584 638 }
585 639
586 640 $sections = $sectionsQuery->get();
587 641
588 642 $data = [
589 - 'sections' => $sections
643 + 'sections' => $sections,
590 644 ];
591 645
592 646 if ($request->get('with_lock_screen')) {
593 647 $data['lockscreen'] = LockscreenService::getLockscreenSettings($course);
@@ -602,13 +656,13 @@
602 656 ->whereHas('course', function ($query) use ($courseId) {
603 657 $query->where('id', $courseId);
604 658 })
605 659 ->where('id', $topicId)
606 - ->with(['lessons'])
660 + ->with([ 'lessons' ])
607 661 ->firstOrFail();
608 662
609 663 $data = [
610 - 'topic' => $topic
664 + 'topic' => $topic,
611 665 ];
612 666
613 667 return apply_filters('fluent_community/admin_course_section_api_response', $data, $request->all());
614 668 }
@@ -621,9 +675,10 @@
621 675 ->whereIn('id', array_keys($indexes))
622 676 ->get();
623 677
624 678 foreach ($sections as $section) {
625 - if (!isset($indexes[$section->id])) continue;
679 + if (!isset($indexes[$section->id])) { continue;
680 + }
626 681 $section->priority = $indexes[$section->id];
627 682 $section->save();
628 683 }
629 684
@@ -628,9 +683,9 @@
628 683 }
629 684
630 685 return [
631 686 'sections' => $sections,
632 - 'message' => __('Section indexes have been updated successfully.', 'fluent-community')
687 + 'message' => __('Section indexes have been updated successfully.', 'fluent-community'),
633 688 ];
634 689 }
635 690
636 691 public function resetLessonIndexes(Request $request, $courseId, $sectionId)
@@ -642,9 +697,10 @@
642 697 ->whereIn('id', array_keys($indexes))
643 698 ->get();
644 699
645 700 foreach ($lessons as $lesson) {
646 - if (!isset($indexes[$lesson->id])) continue;
701 + if (!isset($indexes[$lesson->id])) { continue;
702 + }
647 703 $lesson->priority = $indexes[$lesson->id];
648 704 $lesson->save();
649 705 }
650 706
@@ -649,9 +705,9 @@
649 705 }
650 706
651 707 return [
652 708 'lessons' => $lessons,
653 - 'message' => __('Lesson indexes have been updated successfully.', 'fluent-community')
709 + 'message' => __('Lesson indexes have been updated successfully.', 'fluent-community'),
654 710 ];
655 711 }
656 712
657 713 public function moveLesson(Request $request, $courseId)
@@ -659,18 +715,17 @@
659 715 $lessonId = $request->getSafe('lesson_id', 'intval');
660 716 $sectionId = $request->getSafe('section_id', 'intval');
661 717
662 718 Course::findOrFail($courseId);
663 - CourseTopic::findOrFail($sectionId);
719 + $section = CourseTopic::where('space_id', $courseId)->findOrFail($sectionId);
720 + $lesson = CourseLesson::where('space_id', $courseId)->findOrFail($lessonId);
664 721
665 - $lesson = CourseLesson::findOrFail($lessonId);
666 -
667 722 $lesson->update([
668 - 'parent_id' => $sectionId
723 + 'parent_id' => $section->id,
669 724 ]);
670 725
671 726 return [
672 - 'message' => __('Lesson has been moved successfully', 'fluent-community')
727 + 'message' => __('Lesson has been moved successfully', 'fluent-community'),
673 728 ];
674 729 }
675 730
676 731 public function createSection(Request $request, $courseId)
@@ -675,30 +730,32 @@
675 730
676 731 public function createSection(Request $request, $courseId)
677 732 {
678 733 $this->validate($request->all(), [
679 - 'title' => 'required'
734 + 'title' => 'required',
680 735 ]);
681 736
682 737 $sectionData = [
683 738 'title' => $request->getSafe('title'),
684 739 'space_id' => $courseId,
685 - 'status' => 'published'
740 + 'status' => 'published',
686 741 ];
687 742
688 - Course::findOrFail($courseId);
743 + $course = Course::findOrFail($courseId);
689 744
690 745 $latestPriority = CourseTopic::where('type', 'course_section')->where('space_id', $courseId)->max('priority');
691 746
692 - $sectionData['priority'] = $latestPriority ? $latestPriority + 1 : 0;
747 + $sectionData['priority'] = (int) $latestPriority + 1;
693 748
694 749 $section = CourseTopic::create($sectionData);
695 750
696 751 $section->load('lessons');
697 752
753 + do_action('fluent_community/section/created', $section, $course);
754 +
698 755 return [
699 756 'message' => __('Section has been created successfully.', 'fluent-community'),
700 - 'section' => $section
757 + 'section' => $section,
701 758 ];
702 759 }
703 760
704 761 public function updateSection(Request $request, $courseId, $tipicId)
@@ -704,9 +761,9 @@
704 761 public function updateSection(Request $request, $courseId, $tipicId)
705 762 {
706 763 $this->validate($request->all(), [
707 764 'title' => 'required',
708 - 'status' => 'required|in:draft,published,archived'
765 + 'status' => 'required|in:draft,published,archived',
709 766 ]);
710 767
711 768 Course::findOrFail($courseId);
712 769
@@ -716,9 +773,9 @@
716 773
717 774
718 775 $topicData = [
719 776 'title' => $request->getSafe('title'),
720 - 'status' => $request->get('status')
777 + 'status' => $request->get('status'),
721 778 ];
722 779
723 780 $topic->update($topicData);
724 781
@@ -723,9 +780,9 @@
723 780 $topic->update($topicData);
724 781
725 782 return [
726 783 'message' => __('Topic has been updated successfully.', 'fluent-community'),
727 - 'topic' => $topic
784 + 'topic' => $topic,
728 785 ];
729 786 }
730 787
731 788 public function patchSection(Request $request, $courseId, $tipicId)
@@ -735,21 +792,32 @@
735 792 $topic = CourseTopic::where('space_id', $courseId)
736 793 ->where('id', $tipicId)
737 794 ->firstOrFail();
738 795
739 - $acceptedFields = ['title', 'status'];
796 + $acceptedFields = [ 'title', 'status' ];
740 797
741 798 if ($course->getCourseType() == 'scheduled') {
742 799 $acceptedFields[] = 'scheduled_at';
743 - } else if ($course->getCourseType() == 'structured') {
800 + } elseif ($course->getCourseType() == 'structured') {
744 801 $acceptedFields[] = 'reactions_count';
745 802 }
746 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.
747 807 $topicData = $request->only($acceptedFields);
748 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 +
749 817 if (!empty($topicData['scheduled_at'])) {
750 818 $topic->reactions_count = 0;
751 - } else if (isset($topicData['reactions_count'])) {
819 + } elseif (isset($topicData['reactions_count'])) {
752 820 $topic->scheduled_at = null;
753 821 $topic->reactions_count = $topicData['reactions_count'];
754 822 }
755 823
@@ -774,9 +842,9 @@
774 842 }
775 843
776 844 return [
777 845 'message' => __('Topic has been updated successfully.', 'fluent-community'),
778 - 'topic' => $topic
846 + 'topic' => $topic,
779 847 ];
780 848 }
781 849
782 850 public function copySection(Request $request, $toCourseId)
@@ -787,12 +855,13 @@
787 855 $fromCourse = Course::findOrFail($fromCourseId);
788 856
789 857 if (!$fromCourse->isCourseAdmin()) {
790 858 return $this->sendError([
791 - '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'),
792 860 ]);
793 861 }
794 862
863 + /** @var CourseTopic $originalSection */
795 864 $originalSection = CourseTopic::where('id', $sectionId)
796 865 ->where('space_id', $fromCourseId)
797 866 ->firstOrFail();
798 867
@@ -799,20 +868,26 @@
799 868 $toCourse = Course::findOrFail($toCourseId);
800 869
801 870 $latestPriority = CourseTopic::where('type', 'course_section')->where('space_id', $toCourse->id)->max('priority');
802 871
872 + /** @var CourseTopic $newSection */
803 873 $newSection = $originalSection->replicate();
804 874 $newSection->space_id = $toCourse->id;
805 - $newSection->priority = $latestPriority ? $latestPriority + 1 : 0;
875 + $newSection->priority = (int) $latestPriority + 1;
806 876 $newSection->save();
807 877
878 + do_action('fluent_community/section/created', $newSection, $toCourse);
879 +
808 880 $originalLessons = CourseLesson::where('parent_id', $originalSection->id)->get();
809 881 foreach ($originalLessons as $lesson) {
882 + /** @var CourseLesson $lesson */
810 883 $newLesson = $lesson->replicate();
811 884 $newLesson->space_id = $toCourse->id;
812 885 $newLesson->parent_id = $newSection->id;
813 886 $newLesson->save();
814 887 CourseHelper::copyLessonDocuments($lesson, $newLesson);
888 +
889 + do_action('fluent_community/lesson/created', $newLesson, $newSection);
815 890 }
816 891
817 892 $newSection->load('lessons');
818 893
@@ -817,9 +892,9 @@
817 892 $newSection->load('lessons');
818 893
819 894 return [
820 895 'message' => __('Section has been copied to the selected course', 'fluent-community'),
821 - 'section' => $newSection
896 + 'section' => $newSection,
822 897 ];
823 898 }
824 899
825 900 public function deleteSection(Request $request, $courseId, $sectionId)
@@ -825,9 +900,9 @@
825 900 public function deleteSection(Request $request, $courseId, $sectionId)
826 901 {
827 902 $topic = CourseTopic::where([
828 903 'id' => $sectionId,
829 - 'space_id' => $courseId
904 + 'space_id' => $courseId,
830 905 ])->firstOrFail();
831 906
832 907 do_action('fluent_community/section/before_deleted', $topic);
833 908
@@ -834,9 +909,9 @@
834 909 $topic->delete();
835 910
836 911 $lessons = CourseLesson::where([
837 912 'parent_id' => $sectionId,
838 - 'space_id' => $courseId
913 + 'space_id' => $courseId,
839 914 ])->get();
840 915
841 916 foreach ($lessons as $lesson) {
842 917 do_action('fluent_community/lesson/before_deleted', $lesson);
@@ -843,9 +918,9 @@
843 918 $lesson->delete();
844 919 }
845 920
846 921 return [
847 - 'message' => __('Section has been deleted successfully.', 'fluent-community')
922 + 'message' => __('Section has been deleted successfully.', 'fluent-community'),
848 923 ];
849 924 }
850 925
851 926 public function getLessons(Request $request, $courseId)
@@ -852,9 +927,10 @@
852 927 {
853 928 Course::findOrFail($courseId);
854 929
855 930 $lessons = CourseLesson::where('space_id', $courseId)
856 - ->orderBy('priority', 'ASC');
931 + ->orderBy('priority', 'ASC')
932 + ->orderBy('id', 'ASC');
857 933
858 934 $topicId = (int)$request->get('topic_id');
859 935
860 936 if ($topicId) {
@@ -863,9 +939,9 @@
863 939
864 940 $lessons = $lessons->get();
865 941
866 942 $data = [
867 - 'lessons' => $lessons
943 + 'lessons' => $lessons,
868 944 ];
869 945
870 946 return apply_filters('fluent_community/admin_course_lessons_api_response', $data, $request->all());
871 947 }
@@ -875,23 +951,26 @@
875 951 $lesson = CourseLesson::whereHas('course', function ($query) use ($courseId) {
876 952 $query->where('id', $courseId);
877 953 })
878 954 ->where('id', $lessonId)
879 - ->with(['topic', 'course'])
955 + ->with([ 'topic', 'course' ])
880 956 ->firstOrFail();
881 957
882 958 $data = [
883 - 'lesson' => $lesson
959 + 'lesson' => $lesson,
884 960 ];
885 961
886 962 return apply_filters('fluent_community/admin_course_lesson_api_response', $data, $request->all());
887 963 }
888 964
965 + /**
966 + * Expects `title` and `section_id` at the top level of the request.
967 + */
889 968 public function createLesson(Request $request, $courseId)
890 969 {
891 970 $this->validate($request->all(), [
892 971 'title' => 'required',
893 - 'section_id' => 'required'
972 + 'section_id' => 'required',
894 973 ]);
895 974
896 975 $sectionId = (int)$request->get('section_id');
897 976
@@ -904,9 +983,9 @@
904 983 $lessonData = [
905 984 'title' => $request->getSafe('title'),
906 985 'parent_id' => $topic->id,
907 986 'space_id' => $courseId,
908 - 'status' => 'draft'
987 + 'status' => 'draft',
909 988 ];
910 989
911 990 $latestPriority = CourseLesson::where('type', 'course_lesson')
912 991 ->where('parent_id', $sectionId)
@@ -912,9 +991,9 @@
912 991 ->where('parent_id', $sectionId)
913 992 ->where('space_id', $courseId)
914 993 ->max('priority');
915 994
916 - $lessonData['priority'] = $latestPriority ? $latestPriority + 1 : 0;
995 + $lessonData['priority'] = (int) $latestPriority + 1;
917 996
918 997 $lessonData = apply_filters('fluent_community/lesson/create_data', $lessonData, $request);
919 998
920 999 $lesson = CourseLesson::create($lessonData);
@@ -920,24 +999,29 @@
920 999 $lesson = CourseLesson::create($lessonData);
921 1000
922 1001 $lesson = CourseLesson::findOrFail($lesson->id);
923 1002
1003 + do_action('fluent_community/lesson/created', $lesson, $topic);
1004 +
924 1005 return [
925 1006 'message' => __('Lesson has been created successfully.', 'fluent-community'),
926 - 'lesson' => $lesson
1007 + 'lesson' => $lesson,
927 1008 ];
928 1009 }
929 1010
1011 + /**
1012 + * Expects the fields nested under `lesson`, referencing the section as `parent_id`.
1013 + */
930 1014 public function updateLesson(Request $request, $courseId, $lessionId)
931 1015 {
932 1016 Course::findOrFail($courseId);
933 1017
934 - $lessonData = $request->get('lesson');
1018 + $lessonData = (array)$request->get('lesson');
935 1019
936 1020 $this->validate($lessonData, [
937 1021 'title' => 'required',
938 1022 'parent_id' => 'required',
939 - 'status' => 'required|in:draft,published,archived'
1023 + 'status' => 'required|in:draft,published,archived',
940 1024 ]);
941 1025
942 1026 CourseTopic::whereHas('course', function ($query) use ($courseId) {
943 1027 $query->where('id', $courseId);
@@ -944,8 +1028,9 @@
944 1028 })
945 1029 ->where('id', $lessonData['parent_id'])
946 1030 ->firstOrFail();
947 1031
1032 + /** @var CourseLesson $lesson */
948 1033 $lesson = CourseLesson::where('id', $lessionId)
949 1034 ->where('space_id', $courseId)
950 1035 ->firstOrFail();
951 1036
@@ -953,12 +1038,12 @@
953 1038
954 1039 $updatedMeta = CourseHelper::sanitizeLessonMeta(Arr::get($lessonData, 'meta', []), $lesson);
955 1040 $updatedMeta['document_ids'] = Arr::get($lesson->meta, 'document_ids', []);
956 1041
957 - if ($mediaId = Arr::get($updatedMeta, 'featured_image_id')) {
1042 + if ($mediaId = Arr::get($updatedMeta, 'featured_image_id')) { // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
958 1043 if (!$lesson->isQuizType()) {
959 1044 $media = wp_get_attachment_image_url($mediaId);
960 - $lesson->featured_image = $media ?: null;
1045 + $lesson->featured_image = $media ? $media : null;
961 1046 } else {
962 1047 $media = Helper::getMediaFromUrl(sanitize_url($mediaId));
963 1048 if ($media && !$media->is_active) {
964 1049 Helper::removeMediaByUrl($lesson->featured_image, $lesson->id);
@@ -966,9 +1051,9 @@
966 1051 $media->update([
967 1052 'is_active' => true,
968 1053 'user_id' => get_current_user_id(),
969 1054 'sub_object_id' => $lesson->id,
970 - 'object_source' => 'quiz_thumbnail_' . $lesson->id
1055 + 'object_source' => 'quiz_thumbnail_' . $lesson->id,
971 1056 ]);
972 1057 }
973 1058 }
974 1059 } else {
@@ -976,14 +1061,18 @@
976 1061 $lesson->featured_image = null;
977 1062 }
978 1063
979 1064 $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)
1065 + 'title' => sanitize_text_field(Arr::get($lessonData, 'title')),
1066 + 'status' => Arr::get($lessonData, 'status'),
1067 + 'meta' => wp_parse_args($updatedMeta, $lesson->meta),
984 1068 ]);
985 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 +
986 1075 $updateData = apply_filters('fluent_community/lesson/update_data', $updateData, $lesson);
987 1076
988 1077 $lesson->fill($updateData);
989 1078 $dirtyFields = $lesson->getDirty();
@@ -991,8 +1080,12 @@
991 1080 if ($dirtyFields) {
992 1081 $lesson->save();
993 1082 $isNewlyPublished = $lesson->status === 'published' && $previousStatus !== 'published';
994 1083 do_action('fluent_community/lesson/updated', $lesson, $dirtyFields, $isNewlyPublished);
1084 +
1085 + if ($isNewlyPublished) {
1086 + do_action('fluent_community/lesson/published', $lesson);
1087 + }
995 1088 }
996 1089
997 1090 do_action('fluent_community/lesson/additional_media_updated', $request->all(), $lesson, $updateData);
998 1091
@@ -997,9 +1090,9 @@
997 1090 do_action('fluent_community/lesson/additional_media_updated', $request->all(), $lesson, $updateData);
998 1091
999 1092 return [
1000 1093 'message' => __('Lesson has been updated successfully.', 'fluent-community'),
1001 - 'lesson' => $lesson
1094 + 'lesson' => $lesson,
1002 1095 ];
1003 1096 }
1004 1097
1005 1098 public function patchLesson(Request $request, $courseId, $lessionId)
@@ -1009,12 +1102,39 @@
1009 1102 })
1010 1103 ->where('id', $lessionId)
1011 1104 ->firstOrFail();
1012 1105
1013 - $acceptedFields = ['title', 'status', 'slug'];
1106 + $previousStatus = $lesson->status;
1014 1107
1015 - $lessonData = array_filter($request->only($acceptedFields));
1108 + $acceptedFields = [ 'title', 'status', 'slug' ];
1016 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 +
1017 1137 if (Arr::get($lessonData, 'status') === 'published' && $lesson->status !== 'published') {
1018 1138 if (empty($lesson->scheduled_at)) {
1019 1139 $lessonData['scheduled_at'] = current_time('mysql');
1020 1140 }
@@ -1019,18 +1139,32 @@
1019 1139 $lessonData['scheduled_at'] = current_time('mysql');
1020 1140 }
1021 1141 }
1022 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 +
1023 1148 if (!empty($lessonData)) {
1024 1149 $lesson->fill($lessonData);
1025 - if ($lesson->isDirty()) {
1150 + $dirtyFields = $lesson->getDirty();
1151 +
1152 + if ($dirtyFields) {
1026 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 + }
1027 1161 }
1028 1162 }
1029 1163
1030 1164 return [
1031 1165 'message' => __('Lesson has been updated successfully.', 'fluent-community'),
1032 - 'lesson' => $lesson
1166 + 'lesson' => $lesson,
1033 1167 ];
1034 1168 }
1035 1169
1036 1170 public function deleteLesson(Request $request, $courseId, $lessionId)
@@ -1045,9 +1179,9 @@
1045 1179
1046 1180 $lesson->delete();
1047 1181
1048 1182 return [
1049 - 'message' => __('Lesson has been deleted successfully.', 'fluent-community')
1183 + 'message' => __('Lesson has been deleted successfully.', 'fluent-community'),
1050 1184 ];
1051 1185 }
1052 1186
1053 1187 public function duplicateLesson(Request $request, $courseId, $lessonId)
@@ -1076,13 +1210,14 @@
1076 1210 $duplicateTitle = $lesson->title . ' (Copy)';
1077 1211 if (in_array($duplicateTitle, $existingTitles, true)) {
1078 1212 $counter = 2;
1079 1213 while (in_array($lesson->title . ' (Copy ' . $counter . ')', $existingTitles, true)) {
1080 - $counter++;
1214 + ++$counter;
1081 1215 }
1082 1216 $duplicateTitle = $lesson->title . ' (Copy ' . $counter . ')';
1083 1217 }
1084 1218
1219 + /** @var CourseLesson $newLesson */
1085 1220 $newLesson = $lesson->replicate();
1086 1221 $newLesson->title = $duplicateTitle;
1087 1222 $newLesson->slug = null;
1088 1223 $newLesson->priority = $sourceIndex + 1;
@@ -1088,12 +1223,12 @@
1088 1223 $newLesson->priority = $sourceIndex + 1;
1089 1224 $newLesson->save();
1090 1225
1091 1226 $orderedIds = $siblings->pluck('id')->toArray();
1092 - array_splice($orderedIds, $sourceIndex + 1, 0, [$newLesson->id]);
1227 + array_splice($orderedIds, $sourceIndex + 1, 0, [ $newLesson->id ]);
1093 1228
1094 1229 foreach ($orderedIds as $index => $siblingId) {
1095 - CourseLesson::where('id', $siblingId)->update(['priority' => $index]);
1230 + CourseLesson::where('id', $siblingId)->update([ 'priority' => $index ]);
1096 1231 }
1097 1232
1098 1233 $newLesson = CourseLesson::findOrFail($newLesson->id);
1099 1234
@@ -1098,13 +1233,14 @@
1098 1233 $newLesson = CourseLesson::findOrFail($newLesson->id);
1099 1234
1100 1235 CourseHelper::copyLessonDocuments($lesson, $newLesson);
1101 1236
1237 + do_action('fluent_community/lesson/created', $newLesson, $newLesson->topic);
1102 1238 do_action('fluent_community/lesson/duplicated', $newLesson, $lesson);
1103 1239
1104 1240 return [
1105 1241 'message' => __('Lesson has been duplicated successfully.', 'fluent-community'),
1106 - 'lesson' => $newLesson
1242 + 'lesson' => $newLesson,
1107 1243 ];
1108 1244 }
1109 1245
1110 1246 public function getOtherUsers(Request $request, $courseId)
@@ -1110,9 +1246,9 @@
1110 1246 public function getOtherUsers(Request $request, $courseId)
1111 1247 {
1112 1248 $selects = [
1113 1249 'ID',
1114 - 'display_name'
1250 + 'display_name',
1115 1251 ];
1116 1252
1117 1253 if (current_user_can('list_users')) {
1118 1254 $selects[] = 'user_email';
@@ -1117,9 +1253,9 @@
1117 1253 if (current_user_can('list_users')) {
1118 1254 $selects[] = 'user_email';
1119 1255 }
1120 1256
1121 - $userQuery = User::select(['ID'])
1257 + $userQuery = User::select([ 'ID' ])
1122 1258 ->whereDoesntHave('space_pivot', function ($q) use ($courseId) {
1123 1259 $q->where('space_id', $courseId);
1124 1260 })
1125 1261 ->limit(100)
@@ -1128,9 +1264,9 @@
1128 1264 if (is_multisite()) {
1129 1265 global $wpdb;
1130 1266 $blogId = get_current_blog_id();
1131 1267 $blogPrefix = $wpdb->get_blog_prefix($blogId);
1132 - $userQuery->whereHas('usermeta', function($q) use ($blogPrefix) {
1268 + $userQuery->whereHas('usermeta', function ($q) use ($blogPrefix) {
1133 1269 $q->where('meta_key', $blogPrefix . 'capabilities');
1134 1270 });
1135 1271 }
1136 1272
@@ -1142,9 +1278,9 @@
1142 1278 ->whereIn('ID', $userIds)
1143 1279 ->paginate(100);
1144 1280
1145 1281 $data = [
1146 - 'users' => $users
1282 + 'users' => $users,
1147 1283 ];
1148 1284
1149 1285 return apply_filters('fluent_community/admin_course_non_members_api_response', $data, $request->all());
1150 1286 }
@@ -1164,9 +1300,9 @@
1164 1300 $course->save();
1165 1301
1166 1302 return [
1167 1303 'message' => __('Links have been updated for the course', 'fluent-community'),
1168 - 'links' => $links
1304 + 'links' => $links,
1169 1305 ];
1170 1306 }
1171 1307
1172 1308 public function getMetaSettings(Request $request, $id)
@@ -1175,14 +1311,14 @@
1175 1311 $metaSettings = apply_filters('fluent_community/course/meta_fields', [], $course, $request->all());
1176 1312
1177 1313 if (!$metaSettings) {
1178 1314 return [
1179 - 'meta_settings' => null
1315 + 'meta_settings' => null,
1180 1316 ];
1181 1317 }
1182 1318
1183 1319 return [
1184 - 'meta_settings' => $metaSettings
1320 + 'meta_settings' => $metaSettings,
1185 1321 ];
1186 1322 }
1187 1323
1188 1324 public function getOtherInstructors(Request $request, $courseId)
@@ -1190,15 +1326,24 @@
1190 1326 $search = $request->getSafe('search');
1191 1327
1192 1328 Course::findOrFail($courseId);
1193 1329
1194 - $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)
1195 1340 ->limit(100)
1196 1341 ->searchBy($search)
1197 1342 ->get();
1198 1343
1199 1344 $data = [
1200 - 'instructors' => $instructors
1345 + 'instructors' => $instructors,
1201 1346 ];
1202 1347
1203 1348 return apply_filters('fluent_community/admin_course_other_instructors_api_response', $data, $request->all());
1204 1349 }