← All changes
|
Modules/Course/Http/Controllers/CourseAdminController.php
+49
-8
2.8.1
→
2.10.0
View file →
| @@ -85,9 +85,9 @@ | ||
| 85 | 85 | $courseData = [ |
| 86 | 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,9 +94,9 @@ | ||
| 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 | ], |
| @@ -229,9 +229,9 @@ | ||
| 229 | 229 | |
| 230 | 230 | $courseData = [ |
| 231 | 231 | 'title' => $request->getSafe('title', 'sanitize_text_field'), |
| 232 | 232 | 'privacy' => $request->get('privacy'), |
| 233 | - 'description' => wp_kses_post($request->get('description')), | |
| 233 | + 'description' => wp_kses_post((string) $request->get('description', '')), | |
| 234 | 234 | 'status' => $request->get('status'), |
| 235 | 235 | 'cover_photo' => $request->getSafe('cover_photo', 'sanitize_url'), |
| 236 | 236 | 'parent_id' => $request->get('parent_id') ?: null, // phpcs:ignore Universal.Operators.DisallowShortTernary.Found |
| 237 | 237 | ]; |
| @@ -303,9 +303,9 @@ | ||
| 303 | 303 | $existingSettings['show_instructor_students_count'] = $request->get('settings.show_instructor_students_count') === 'yes' ? 'yes' : 'no'; |
| 304 | 304 | $existingSettings['show_paywalls'] = $request->get('settings.show_paywalls') === 'yes' ? 'yes' : 'no'; |
| 305 | 305 | $existingSettings['show_welcome_banner'] = $request->get('settings.show_welcome_banner') === 'yes' ? 'yes' : 'no'; |
| 306 | 306 | $existingSettings['course_layout'] = $request->get('settings.course_layout') === 'modern' ? 'modern' : 'classic'; |
| 307 | - $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 | 308 | $existingSettings['sequential_lesson_order'] = $request->get('settings.sequential_lesson_order') === 'yes' ? 'yes' : 'no'; |
| 309 | 309 | |
| 310 | 310 | if ($request->get('privacy') == 'public' && $existingSettings['course_type'] == 'self_paced') { |
| 311 | 311 | $existingSettings['public_lesson_view'] = $request->get('settings.public_lesson_view') == 'yes' ? 'yes' : 'no'; |
| @@ -383,8 +383,10 @@ | ||
| 383 | 383 | $newTopic = $topic->replicate(); |
| 384 | 384 | $newTopic->space_id = $newCourse->id; |
| 385 | 385 | $newTopic->save(); |
| 386 | 386 | |
| 387 | + do_action('fluent_community/section/created', $newTopic, $newCourse); | |
| 388 | + | |
| 387 | 389 | foreach ($topic->lessons as $lesson) { |
| 388 | 390 | /** @var CourseLesson $lesson */ |
| 389 | 391 | $newLesson = $lesson->replicate(); |
| 390 | 392 | $newLesson->space_id = $newCourse->id; |
| @@ -390,8 +392,10 @@ | ||
| 390 | 392 | $newLesson->space_id = $newCourse->id; |
| 391 | 393 | $newLesson->parent_id = $newTopic->id; |
| 392 | 394 | $newLesson->save(); |
| 393 | 395 | CourseHelper::copyLessonDocuments($lesson, $newLesson); |
| 396 | + | |
| 397 | + do_action('fluent_community/lesson/created', $newLesson, $newTopic); | |
| 394 | 398 | } |
| 395 | 399 | } |
| 396 | 400 | |
| 397 | 401 | do_action('fluent_community/course/created', $newCourse); |
| @@ -735,9 +739,9 @@ | ||
| 735 | 739 | 'space_id' => $courseId, |
| 736 | 740 | 'status' => 'published', |
| 737 | 741 | ]; |
| 738 | 742 | |
| 739 | - Course::findOrFail($courseId); | |
| 743 | + $course = Course::findOrFail($courseId); | |
| 740 | 744 | |
| 741 | 745 | $latestPriority = CourseTopic::where('type', 'course_section')->where('space_id', $courseId)->max('priority'); |
| 742 | 746 | |
| 743 | 747 | $sectionData['priority'] = (int) $latestPriority + 1; |
| @@ -745,8 +749,10 @@ | ||
| 745 | 749 | $section = CourseTopic::create($sectionData); |
| 746 | 750 | |
| 747 | 751 | $section->load('lessons'); |
| 748 | 752 | |
| 753 | + do_action('fluent_community/section/created', $section, $course); | |
| 754 | + | |
| 749 | 755 | return [ |
| 750 | 756 | 'message' => __('Section has been created successfully.', 'fluent-community'), |
| 751 | 757 | 'section' => $section, |
| 752 | 758 | ]; |
| @@ -868,8 +874,10 @@ | ||
| 868 | 874 | $newSection->space_id = $toCourse->id; |
| 869 | 875 | $newSection->priority = (int) $latestPriority + 1; |
| 870 | 876 | $newSection->save(); |
| 871 | 877 | |
| 878 | + do_action('fluent_community/section/created', $newSection, $toCourse); | |
| 879 | + | |
| 872 | 880 | $originalLessons = CourseLesson::where('parent_id', $originalSection->id)->get(); |
| 873 | 881 | foreach ($originalLessons as $lesson) { |
| 874 | 882 | /** @var CourseLesson $lesson */ |
| 875 | 883 | $newLesson = $lesson->replicate(); |
| @@ -876,8 +884,10 @@ | ||
| 876 | 884 | $newLesson->space_id = $toCourse->id; |
| 877 | 885 | $newLesson->parent_id = $newSection->id; |
| 878 | 886 | $newLesson->save(); |
| 879 | 887 | CourseHelper::copyLessonDocuments($lesson, $newLesson); |
| 888 | + | |
| 889 | + do_action('fluent_community/lesson/created', $newLesson, $newSection); | |
| 880 | 890 | } |
| 881 | 891 | |
| 882 | 892 | $newSection->load('lessons'); |
| 883 | 893 | |
| @@ -951,8 +961,11 @@ | ||
| 951 | 961 | |
| 952 | 962 | return apply_filters('fluent_community/admin_course_lesson_api_response', $data, $request->all()); |
| 953 | 963 | } |
| 954 | 964 | |
| 965 | + /** | |
| 966 | + * Expects `title` and `section_id` at the top level of the request. | |
| 967 | + */ | |
| 955 | 968 | public function createLesson(Request $request, $courseId) |
| 956 | 969 | { |
| 957 | 970 | $this->validate($request->all(), [ |
| 958 | 971 | 'title' => 'required', |
| @@ -986,8 +999,10 @@ | ||
| 986 | 999 | $lesson = CourseLesson::create($lessonData); |
| 987 | 1000 | |
| 988 | 1001 | $lesson = CourseLesson::findOrFail($lesson->id); |
| 989 | 1002 | |
| 1003 | + do_action('fluent_community/lesson/created', $lesson, $topic); | |
| 1004 | + | |
| 990 | 1005 | return [ |
| 991 | 1006 | 'message' => __('Lesson has been created successfully.', 'fluent-community'), |
| 992 | 1007 | 'lesson' => $lesson, |
| 993 | 1008 | ]; |
| @@ -992,13 +1007,16 @@ | ||
| 992 | 1007 | 'lesson' => $lesson, |
| 993 | 1008 | ]; |
| 994 | 1009 | } |
| 995 | 1010 | |
| 1011 | + /** | |
| 1012 | + * Expects the fields nested under `lesson`, referencing the section as `parent_id`. | |
| 1013 | + */ | |
| 996 | 1014 | public function updateLesson(Request $request, $courseId, $lessionId) |
| 997 | 1015 | { |
| 998 | 1016 | Course::findOrFail($courseId); |
| 999 | 1017 | |
| 1000 | - $lessonData = $request->get('lesson'); | |
| 1018 | + $lessonData = (array)$request->get('lesson'); | |
| 1001 | 1019 | |
| 1002 | 1020 | $this->validate($lessonData, [ |
| 1003 | 1021 | 'title' => 'required', |
| 1004 | 1022 | 'parent_id' => 'required', |
| @@ -1062,8 +1080,12 @@ | ||
| 1062 | 1080 | if ($dirtyFields) { |
| 1063 | 1081 | $lesson->save(); |
| 1064 | 1082 | $isNewlyPublished = $lesson->status === 'published' && $previousStatus !== 'published'; |
| 1065 | 1083 | do_action('fluent_community/lesson/updated', $lesson, $dirtyFields, $isNewlyPublished); |
| 1084 | + | |
| 1085 | + if ($isNewlyPublished) { | |
| 1086 | + do_action('fluent_community/lesson/published', $lesson); | |
| 1087 | + } | |
| 1066 | 1088 | } |
| 1067 | 1089 | |
| 1068 | 1090 | do_action('fluent_community/lesson/additional_media_updated', $request->all(), $lesson, $updateData); |
| 1069 | 1091 | |
| @@ -1080,8 +1102,10 @@ | ||
| 1080 | 1102 | }) |
| 1081 | 1103 | ->where('id', $lessionId) |
| 1082 | 1104 | ->firstOrFail(); |
| 1083 | 1105 | |
| 1106 | + $previousStatus = $lesson->status; | |
| 1107 | + | |
| 1084 | 1108 | $acceptedFields = [ 'title', 'status', 'slug' ]; |
| 1085 | 1109 | |
| 1086 | 1110 | // empty title/slug/status must not overwrite, but a literal "0" is a valid value |
| 1087 | 1111 | // Request::only() is a plain array pick with no sanitization, so each field is |
| @@ -1095,9 +1119,16 @@ | ||
| 1095 | 1119 | $lessonData['title'] = sanitize_text_field($lessonData['title']); |
| 1096 | 1120 | } |
| 1097 | 1121 | |
| 1098 | 1122 | if (isset($lessonData['slug'])) { |
| 1099 | - $lessonData['slug'] = sanitize_title($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 | + ); | |
| 1100 | 1131 | } |
| 1101 | 1132 | |
| 1102 | 1133 | if (isset($lessonData['status']) && !in_array($lessonData['status'], ['draft', 'published', 'archived'], true)) { |
| 1103 | 1134 | unset($lessonData['status']); |
| @@ -1115,10 +1146,19 @@ | ||
| 1115 | 1146 | } |
| 1116 | 1147 | |
| 1117 | 1148 | if (!empty($lessonData)) { |
| 1118 | 1149 | $lesson->fill($lessonData); |
| 1119 | - if ($lesson->isDirty()) { | |
| 1150 | + $dirtyFields = $lesson->getDirty(); | |
| 1151 | + | |
| 1152 | + if ($dirtyFields) { | |
| 1120 | 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 | + } | |
| 1121 | 1161 | } |
| 1122 | 1162 | } |
| 1123 | 1163 | |
| 1124 | 1164 | return [ |
| @@ -1193,8 +1233,9 @@ | ||
| 1193 | 1233 | $newLesson = CourseLesson::findOrFail($newLesson->id); |
| 1194 | 1234 | |
| 1195 | 1235 | CourseHelper::copyLessonDocuments($lesson, $newLesson); |
| 1196 | 1236 | |
| 1237 | + do_action('fluent_community/lesson/created', $newLesson, $newLesson->topic); | |
| 1197 | 1238 | do_action('fluent_community/lesson/duplicated', $newLesson, $lesson); |
| 1198 | 1239 | |
| 1199 | 1240 | return [ |
| 1200 | 1241 | 'message' => __('Lesson has been duplicated successfully.', 'fluent-community'), |