PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.4
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.4
4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 4.2.1 All 138 releases
learnpress / inc / MCP / Support / Curriculum.php

Curriculum.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.4, at inc/MCP/Support/Curriculum.php

225 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace LearnPress\MCP\Support;
4
5 use LearnPress\Models\CourseModel;
6 use LearnPress\Models\CoursePostModel;
7 use LearnPress\Models\CourseSectionItemModel;
8
9 defined( 'ABSPATH' ) || exit;
10
11 /**
12 * Curriculum-relationship helpers for MCP write tools.
13 *
14 * Resolves where a lesson/quiz lives in a course so delete tools can record
15 * reversible recovery metadata before trashing a post.
16 */
17 class Curriculum {
18
19 /**
20 * Locate a curriculum item (lesson/quiz) inside a course.
21 *
22 * @param CourseModel $course Course model.
23 * @param int $item_id Lesson/quiz post ID.
24 *
25 * @return array|null { section_id, section_name, item_order, item_type } or null.
26 */
27 public static function locate_item( CourseModel $course, int $item_id ) {
28 foreach ( $course->get_section_items() as $section ) {
29 $section_id = absint( $section->section_id ?? 0 );
30 $section_name = (string) ( $section->section_name ?? '' );
31 $items = is_array( $section->items ?? null ) ? $section->items : array();
32
33 foreach ( $items as $item ) {
34 if ( absint( $item->item_id ?? 0 ) === $item_id ) {
35 return array(
36 'section_id' => $section_id,
37 'section_name' => $section_name,
38 'item_order' => absint( $item->item_order ?? 0 ),
39 'item_type' => (string) ( $item->item_type ?? '' ),
40 );
41 }
42 }
43 }
44
45 return null;
46 }
47
48 /**
49 * Resolve the first course ID that contains a given curriculum item.
50 *
51 * @param int $item_id Lesson/quiz post ID.
52 * @param string $item_type Item post type.
53 *
54 * @return int 0 when the item is not assigned to any course.
55 */
56 public static function find_course_id_for_item( int $item_id, string $item_type ): int {
57 $rows = CourseSectionItemModel::get_courses_from_item_id( $item_id, $item_type );
58 foreach ( $rows as $row ) {
59 $course_id = absint( $row->section_course_id ?? 0 );
60 if ( $course_id > 0 ) {
61 return $course_id;
62 }
63 }
64
65 return 0;
66 }
67
68 /**
69 * Remove a lesson/quiz from a course section (relationship-only).
70 *
71 * @param int $section_id Section ID.
72 * @param int $item_id Item ID.
73 * @param int $course_id Course ID.
74 *
75 * @return bool True when a relationship row was found and removed.
76 */
77 public static function remove_item_from_section( int $section_id, int $item_id, int $course_id = 0 ): bool {
78 $relation = CourseSectionItemModel::find( $section_id, $item_id, true );
79 if ( ! $relation instanceof CourseSectionItemModel ) {
80 return false;
81 }
82
83 // CourseSectionItemModel::find() does not populate section_course_id, which
84 // delete() needs for its capability check (otherwise it resolves course 0 and
85 // CourseSectionItemModel::get_course_model() throws a TypeError). Supply it.
86 if ( $course_id > 0 ) {
87 $relation->section_course_id = $course_id;
88 }
89
90 $relation->delete();
91
92 return true;
93 }
94
95 /**
96 * Resolve the curriculum location of a lesson/quiz.
97 *
98 * @param int $item_id Item post ID.
99 * @param string $item_type Item post type.
100 * @param int $hint_course_id Optional known course ID.
101 *
102 * @return array { course_id, section_id, section_name, item_order }
103 */
104 public static function resolve_location( int $item_id, string $item_type, int $hint_course_id = 0 ): array {
105 $location = array(
106 'course_id' => 0,
107 'section_id' => 0,
108 'section_name' => '',
109 'item_order' => 0,
110 );
111
112 $course_id = $hint_course_id > 0 ? $hint_course_id : self::find_course_id_for_item( $item_id, $item_type );
113 if ( $course_id <= 0 ) {
114 return $location;
115 }
116 $location['course_id'] = $course_id;
117
118 $course = CourseModel::find( $course_id, true );
119 if ( ! $course instanceof CourseModel ) {
120 return $location;
121 }
122
123 $found = self::locate_item( $course, $item_id );
124 if ( is_array( $found ) ) {
125 $location['section_id'] = (int) $found['section_id'];
126 $location['section_name'] = (string) $found['section_name'];
127 $location['item_order'] = (int) $found['item_order'];
128 }
129
130 return $location;
131 }
132
133 /**
134 * Place (move/reorder) a curriculum item to a 1-based position in a section.
135 *
136 * Works for same-section reordering (old == new) and cross-section moves.
137 *
138 * @param CoursePostModel $course_post Course post model.
139 * @param int $course_id Course ID.
140 * @param int $item_id Item post ID.
141 * @param int $new_section_id Target section ID.
142 * @param int $old_section_id Current section ID.
143 * @param int $position Desired 1-based position; <= 0 appends.
144 *
145 * @return void
146 */
147 public static function place_item(
148 CoursePostModel $course_post,
149 int $course_id,
150 int $item_id,
151 int $new_section_id,
152 int $old_section_id,
153 int $position
154 ): void {
155 $course = CourseModel::find( $course_id, false );
156 $ids = array();
157 if ( $course instanceof CourseModel ) {
158 $course->sections_items = null; // Force a fresh read from the relationship tables, not the JSON snapshot.
159 foreach ( $course->get_section_items() as $section ) {
160 if ( absint( $section->section_id ?? 0 ) !== $new_section_id ) {
161 continue;
162 }
163 $section_items = is_array( $section->items ?? null ) ? $section->items : array();
164 foreach ( $section_items as $item ) {
165 $ids[] = absint( $item->item_id ?? 0 );
166 }
167 }
168 }
169
170 $ids = array_values( array_filter( $ids ) );
171 $ids = array_values( array_diff( $ids, array( $item_id ) ) );
172
173 if ( $position > 0 ) {
174 $index = max( 0, min( count( $ids ), $position - 1 ) );
175 array_splice( $ids, $index, 0, array( $item_id ) );
176 } else {
177 $ids[] = $item_id;
178 }
179
180 $course_post->update_items_position(
181 array(
182 'items_position' => $ids,
183 'item_id_change' => $item_id,
184 'section_id_new_of_item' => $new_section_id,
185 'section_id_old_of_item' => $old_section_id,
186 )
187 );
188 }
189
190 /**
191 * Collect a course's curriculum items by type, optionally filtered by section.
192 *
193 * @param CourseModel $course Course model.
194 * @param string $item_type LearnPress item type.
195 * @param int $section_id Optional section filter.
196 *
197 * @return array
198 */
199 public static function collect_items( CourseModel $course, string $item_type, int $section_id = 0 ): array {
200 $items = array();
201 foreach ( $course->get_section_items() as $section ) {
202 $current_section_id = absint( $section->section_id ?? $section->id ?? 0 );
203 if ( $section_id > 0 && $section_id !== $current_section_id ) {
204 continue;
205 }
206
207 foreach ( $section->items as $item ) {
208 $current_type = (string) ( $item->item_type ?? $item->type ?? '' );
209 if ( $item_type !== $current_type ) {
210 continue;
211 }
212 $items[] = array(
213 'course_id' => $course->get_id(),
214 'section_id' => $current_section_id,
215 'section_name' => (string) ( $section->section_name ?? $section->title ?? '' ),
216 'item_id' => absint( $item->item_id ?? $item->id ?? 0 ),
217 'preview' => ! empty( $item->preview ),
218 );
219 }
220 }
221
222 return $items;
223 }
224 }
225