PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.3.9.1
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.3.9.1
4.4.8 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 All 139 releases
learnpress / inc / MCP / Concerns / AbilityExecutors.php

AbilityExecutors.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.3.9.1, at inc/MCP/Concerns/AbilityExecutors.php

509 lines 17.1 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\Concerns;
4
5 use LearnPress\Databases\UserItemsDB;
6 use LearnPress\Filters\Course\CourseJsonFilter;
7 use LearnPress\Filters\UserItemsFilter;
8 use LearnPress\Models\CourseModel;
9 use LearnPress\Models\CoursePostModel;
10 use LearnPress\Models\Courses;
11 use LearnPress\Models\LessonPostModel;
12 use LearnPress\Models\QuizPostModel;
13 use LearnPress\Models\UserItems\UserCourseModel;
14 use LearnPress\Models\UserModel;
15 use LP_Helper;
16 use LP_Material_Files_DB;
17 use Throwable;
18 use WP_Error;
19
20 defined( 'ABSPATH' ) || exit;
21
22 /**
23 * Ability execute callbacks for LearnPress MCP read-only abilities.
24 */
25 trait AbilityExecutors {
26 /**
27 * Execute `learnpress/get-courses`.
28 *
29 * @param mixed $input Ability input.
30 *
31 * @return array|WP_Error
32 */
33 public static function execute_get_courses( $input ) {
34 $args = self::input_arr( $input, 'learnpress/get-courses' );
35 if ( is_wp_error( $args ) ) {
36 return $args;
37 }
38
39 $page = self::page( $args['page'] ?? 1 );
40 $per_page = self::per_page( $args['per_page'] ?? 10 );
41 $filter = new CourseJsonFilter();
42 $filter->page = $page;
43 $filter->limit = $per_page;
44 $filter->post_status = self::status_list( $args['status'] ?? array( 'publish' ) );
45 if ( empty( $filter->post_status ) ) {
46 $filter->post_status = array( 'publish' );
47 }
48
49 if ( ! empty( $args['category'] ) ) {
50 $filter->term_ids = array( absint( $args['category'] ) );
51 }
52 if ( ! empty( $args['instructor'] ) ) {
53 $filter->post_author = absint( $args['instructor'] );
54 }
55 if ( isset( $args['search'] ) ) {
56 $filter->post_title = LP_Helper::sanitize_params_submitted( (string) $args['search'] );
57 }
58
59 $price_min = isset( $args['price_min'] ) && is_numeric( $args['price_min'] ) ? (float) $args['price_min'] : null;
60 $price_max = isset( $args['price_max'] ) && is_numeric( $args['price_max'] ) ? (float) $args['price_max'] : null;
61 if ( null !== $price_min && $price_min < 0 ) {
62 return self::invalid( __( 'price_min must be greater than or equal to 0.', 'learnpress' ) );
63 }
64 if ( null !== $price_max && $price_max < 0 ) {
65 return self::invalid( __( 'price_max must be greater than or equal to 0.', 'learnpress' ) );
66 }
67 if ( null !== $price_min && null !== $price_max && $price_min > $price_max ) {
68 return self::invalid( __( 'price_min must not be greater than price_max.', 'learnpress' ) );
69 }
70
71 global $wpdb;
72 if ( null !== $price_min && $wpdb ) {
73 $filter->where[] = $wpdb->prepare( 'AND c.price_to_sort >= %f', $price_min );
74 }
75 if ( null !== $price_max && $wpdb ) {
76 $filter->where[] = $wpdb->prepare( 'AND c.price_to_sort <= %f', $price_max );
77 }
78
79 try {
80 $total_rows = 0;
81 $rows = Courses::get_list_courses( $filter, $total_rows );
82 $rows = is_array( $rows ) ? $rows : array();
83 $items = array();
84 foreach ( $rows as $row ) {
85 $course = CourseModel::find( absint( $row->ID ?? 0 ), true );
86 if ( $course instanceof CourseModel ) {
87 $items[] = self::course_summary( $course );
88 }
89 }
90
91 return array(
92 'items' => $items,
93 'pagination' => array(
94 'page' => $page,
95 'per_page' => $per_page,
96 'total_items' => (int) $total_rows,
97 'total_pages' => self::total_pages( (int) $total_rows, $per_page ),
98 ),
99 );
100 } catch ( Throwable $e ) {
101 return self::internal();
102 }
103 }
104
105 /**
106 * Execute `learnpress/get-course-details`.
107 *
108 * @param mixed $input Ability input.
109 *
110 * @return array|WP_Error
111 */
112 public static function execute_get_course_details( $input ) {
113 $args = self::input_arr( $input, 'learnpress/get-course-details' );
114 if ( is_wp_error( $args ) ) {
115 return $args;
116 }
117
118 $course_id = absint( $args['course_id'] ?? 0 );
119 if ( $course_id <= 0 ) {
120 return self::invalid( __( 'course_id is required and must be a positive integer.', 'learnpress' ) );
121 }
122
123 $course = CourseModel::find( $course_id, true );
124 if ( ! $course instanceof CourseModel ) {
125 return self::not_found( __( 'Course not found.', 'learnpress' ) );
126 }
127
128 $sections = array();
129 foreach ( $course->get_section_items() as $section ) {
130 $items = array();
131 foreach ( $section->items as $item ) {
132 $items[] = array(
133 'item_id' => absint( $item->item_id ?? $item->id ?? 0 ),
134 'item_type' => (string) ( $item->item_type ?? $item->type ?? '' ),
135 'title' => (string) ( $item->title ?? '' ),
136 'preview' => ! empty( $item->preview ),
137 );
138 }
139 $sections[] = array(
140 'section_id' => absint( $section->section_id ?? $section->id ?? 0 ),
141 'section_name' => (string) ( $section->section_name ?? $section->title ?? '' ),
142 'section_description' => (string) ( $section->section_description ?? $section->description ?? '' ),
143 'items' => $items,
144 );
145 }
146
147 $detail = self::course_summary( $course );
148 $detail['description'] = (string) $course->get_description();
149 $detail['requirements'] = (string) $course->get_meta_value_by_key( CoursePostModel::META_KEY_REQUIREMENTS, '' );
150 $detail['curriculum'] = array(
151 'sections' => $sections,
152 'items_count' => (int) $course->count_items(),
153 );
154
155 return array( 'course' => $detail );
156 }
157
158 /**
159 * Execute `learnpress/list-lessons`.
160 *
161 * @param mixed $input Ability input.
162 *
163 * @return array|WP_Error
164 */
165 public static function execute_list_lessons( $input ) {
166 $args = self::input_arr( $input, 'learnpress/list-lessons' );
167 if ( is_wp_error( $args ) ) {
168 return $args;
169 }
170
171 $course_id = absint( $args['course_id'] ?? 0 );
172 if ( $course_id <= 0 ) {
173 return self::invalid( __( 'course_id is required and must be a positive integer.', 'learnpress' ) );
174 }
175
176 $course = CourseModel::find( $course_id, true );
177 if ( ! $course instanceof CourseModel ) {
178 return self::not_found( __( 'Course not found.', 'learnpress' ) );
179 }
180
181 $section_id = absint( $args['section_id'] ?? 0 );
182 $page = self::page( $args['page'] ?? 1 );
183 $per_page = self::per_page( $args['per_page'] ?? 10 );
184 $statuses = self::status_list( $args['status'] ?? null );
185 $refs = self::collect_items( $course, LP_LESSON_CPT, $section_id );
186
187 $all = array();
188 foreach ( $refs as $ref ) {
189 $lesson = LessonPostModel::find( (int) $ref['item_id'], true );
190 if ( ! $lesson instanceof LessonPostModel ) {
191 continue;
192 }
193 if ( ! empty( $statuses ) && ! in_array( $lesson->post_status, $statuses, true ) ) {
194 continue;
195 }
196 $all[] = self::lesson_summary( $lesson, $ref );
197 }
198
199 $total = count( $all );
200 $items = array_slice( $all, ( $page - 1 ) * $per_page, $per_page );
201
202 return array(
203 'items' => array_values( $items ),
204 'pagination' => array(
205 'page' => $page,
206 'per_page' => $per_page,
207 'total_items' => $total,
208 'total_pages' => self::total_pages( $total, $per_page ),
209 ),
210 );
211 }
212
213 /**
214 * Execute `learnpress/get-lesson-details`.
215 *
216 * @param mixed $input Ability input.
217 *
218 * @return array|WP_Error
219 */
220 public static function execute_get_lesson_details( $input ) {
221 $args = self::input_arr( $input, 'learnpress/get-lesson-details' );
222 if ( is_wp_error( $args ) ) {
223 return $args;
224 }
225
226 $lesson_id = absint( $args['lesson_id'] ?? 0 );
227 if ( $lesson_id <= 0 ) {
228 return self::invalid( __( 'lesson_id is required and must be a positive integer.', 'learnpress' ) );
229 }
230
231 $lesson = LessonPostModel::find( $lesson_id, true );
232 if ( ! $lesson instanceof LessonPostModel ) {
233 return self::not_found( __( 'Lesson not found.', 'learnpress' ) );
234 }
235
236 $materials = array();
237 if ( class_exists( 'LP_Material_Files_DB' ) ) {
238 $materials_rs = LP_Material_Files_DB::getInstance()->get_material_by_item_id( $lesson_id, 0, 0, false );
239 $materials_rs = is_array( $materials_rs ) ? $materials_rs : array();
240 foreach ( $materials_rs as $material ) {
241 $materials[] = array(
242 'file_id' => absint( $material->file_id ?? 0 ),
243 'name' => (string) ( $material->file_name ?? '' ),
244 'type' => (string) ( $material->file_type ?? '' ),
245 'method' => (string) ( $material->method ?? '' ),
246 'file_path' => (string) ( $material->file_path ?? '' ),
247 'url' => (string) ( $material->file_url ?? '' ),
248 );
249 }
250 }
251
252 return array(
253 'lesson' => array(
254 'lesson_id' => $lesson->get_id(),
255 'title' => (string) $lesson->get_the_title(),
256 'content' => (string) $lesson->get_the_content(),
257 'excerpt' => (string) $lesson->get_the_excerpt(),
258 'duration' => (string) $lesson->get_duration(),
259 'video_intro' => (string) $lesson->get_meta_value_by_key( '_lp_lesson_video_intro', '' ),
260 'preview' => (bool) $lesson->has_preview(),
261 'status' => (string) $lesson->post_status,
262 'permalink' => (string) $lesson->get_permalink(),
263 'materials' => $materials,
264 'materials_no' => count( $materials ),
265 ),
266 );
267 }
268
269 /**
270 * Execute `learnpress/list-quizzes`.
271 *
272 * @param mixed $input Ability input.
273 *
274 * @return array|WP_Error
275 */
276 public static function execute_list_quizzes( $input ) {
277 $args = self::input_arr( $input, 'learnpress/list-quizzes' );
278 if ( is_wp_error( $args ) ) {
279 return $args;
280 }
281
282 $course_id = absint( $args['course_id'] ?? 0 );
283 if ( $course_id <= 0 ) {
284 return self::invalid( __( 'course_id is required and must be a positive integer.', 'learnpress' ) );
285 }
286
287 $course = CourseModel::find( $course_id, true );
288 if ( ! $course instanceof CourseModel ) {
289 return self::not_found( __( 'Course not found.', 'learnpress' ) );
290 }
291
292 $page = self::page( $args['page'] ?? 1 );
293 $per_page = self::per_page( $args['per_page'] ?? 10 );
294 $refs = self::collect_items( $course, LP_QUIZ_CPT );
295
296 $all = array();
297 foreach ( $refs as $ref ) {
298 $quiz = QuizPostModel::find( (int) $ref['item_id'], true );
299 if ( $quiz instanceof QuizPostModel ) {
300 $all[] = self::quiz_summary( $quiz, $ref );
301 }
302 }
303
304 $total = count( $all );
305 $items = array_slice( $all, ( $page - 1 ) * $per_page, $per_page );
306
307 return array(
308 'items' => array_values( $items ),
309 'pagination' => array(
310 'page' => $page,
311 'per_page' => $per_page,
312 'total_items' => $total,
313 'total_pages' => self::total_pages( $total, $per_page ),
314 ),
315 );
316 }
317
318 /**
319 * Execute `learnpress/get-quiz-details`.
320 *
321 * @param mixed $input Ability input.
322 *
323 * @return array|WP_Error
324 */
325 public static function execute_get_quiz_details( $input ) {
326 $args = self::input_arr( $input, 'learnpress/get-quiz-details' );
327 if ( is_wp_error( $args ) ) {
328 return $args;
329 }
330
331 $quiz_id = absint( $args['quiz_id'] ?? 0 );
332 if ( $quiz_id <= 0 ) {
333 return self::invalid( __( 'quiz_id is required and must be a positive integer.', 'learnpress' ) );
334 }
335
336 $quiz = QuizPostModel::find( $quiz_id, true );
337 if ( ! $quiz instanceof QuizPostModel ) {
338 return self::not_found( __( 'Quiz not found.', 'learnpress' ) );
339 }
340
341 return array(
342 'quiz' => array(
343 'quiz_id' => $quiz->get_id(),
344 'title' => (string) $quiz->get_the_title(),
345 'excerpt' => (string) $quiz->get_the_excerpt(),
346 'status' => (string) $quiz->post_status,
347 'permalink' => (string) $quiz->get_permalink(),
348 'duration' => (string) $quiz->get_duration(),
349 'passing_grade' => (float) $quiz->get_passing_grade(),
350 'retake_count' => (int) $quiz->get_retake_count(),
351 'questions_count' => (int) $quiz->count_questions(),
352 'mark' => (float) $quiz->get_mark(),
353 'instant_check' => (bool) $quiz->has_instant_check(),
354 'negative_marking' => (bool) $quiz->has_negative_marking(),
355 'minus_skip_questions' => (bool) $quiz->has_minus_skip_questions(),
356 'show_correct_review' => (bool) $quiz->has_show_correct_review(),
357 ),
358 );
359 }
360
361 /**
362 * Execute `learnpress/get-student-progress`.
363 *
364 * @param mixed $input Ability input.
365 *
366 * @return array|WP_Error
367 */
368 public static function execute_get_student_progress( $input ) {
369 $args = self::input_arr( $input, 'learnpress/get-student-progress' );
370 if ( is_wp_error( $args ) ) {
371 return $args;
372 }
373
374 $user_id = absint( $args['user_id'] ?? 0 );
375 $course_id = absint( $args['course_id'] ?? 0 );
376 if ( $user_id <= 0 || $course_id <= 0 ) {
377 return self::invalid( __( 'user_id and course_id are required positive integers.', 'learnpress' ) );
378 }
379
380 try {
381 $user_course = UserCourseModel::find( $user_id, $course_id, true );
382 if ( ! $user_course instanceof UserCourseModel ) {
383 return self::not_found( __( 'Enrollment not found for this user and course.', 'learnpress' ) );
384 }
385
386 $course = CourseModel::find( $course_id, true );
387 $user = UserModel::find( $user_id, true );
388 $results = $user_course->calculate_course_results();
389 $count = (int) ( $results['count_items'] ?? 0 );
390 $done = (int) ( $results['completed_items'] ?? 0 );
391 $percent = $count > 0 ? round( ( $done * 100 ) / $count, 2 ) : 0;
392
393 return array(
394 'progress' => array(
395 'user' => array(
396 'user_id' => $user_id,
397 'display_name' => $user instanceof UserModel ? $user->get_display_name() : '',
398 'email' => $user instanceof UserModel ? $user->get_email() : '',
399 ),
400 'course' => array(
401 'course_id' => $course_id,
402 'title' => $course instanceof CourseModel ? $course->get_title() : '',
403 ),
404 'enrollment' => array(
405 'status' => (string) $user_course->status,
406 'graduation' => (string) $user_course->graduation,
407 'start_time' => (string) $user_course->start_time,
408 'end_time' => (string) $user_course->end_time,
409 ),
410 'result' => array(
411 'count_items' => $count,
412 'completed_items' => $done,
413 'progress_percent' => $percent,
414 'evaluate_type' => (string) ( $results['evaluate_type'] ?? '' ),
415 'pass' => (int) ( $results['pass'] ?? 0 ),
416 'result' => (float) ( $results['result'] ?? 0 ),
417 'items' => (array) ( $results['items'] ?? array() ),
418 ),
419 ),
420 );
421 } catch ( Throwable $e ) {
422 return self::internal();
423 }
424 }
425
426 /**
427 * Execute `learnpress/get-enrollments`.
428 *
429 * @param mixed $input Ability input.
430 *
431 * @return array|WP_Error
432 */
433 public static function execute_get_enrollments( $input ) {
434 $args = self::input_arr( $input, 'learnpress/get-enrollments' );
435 if ( is_wp_error( $args ) ) {
436 return $args;
437 }
438
439 $page = self::page( $args['page'] ?? 1 );
440 $per_page = self::per_page( $args['per_page'] ?? 10 );
441 $filter = new UserItemsFilter();
442 $filter->item_type = LP_COURSE_CPT;
443 $filter->page = $page;
444 $filter->limit = $per_page;
445 $filter->order_by = 'ui.user_item_id';
446 $filter->order = 'DESC';
447
448 if ( ! empty( $args['course_id'] ) ) {
449 $filter->item_id = absint( $args['course_id'] );
450 }
451 if ( ! empty( $args['user_id'] ) ) {
452 $filter->user_id = absint( $args['user_id'] );
453 }
454
455 $statuses = self::status_list( $args['status'] ?? null );
456 if ( count( $statuses ) === 1 ) {
457 $filter->status = $statuses[0];
458 } elseif ( count( $statuses ) > 1 ) {
459 $filter->statues = $statuses;
460 }
461
462 try {
463 $total_rows = 0;
464 $rows = UserItemsDB::getInstance()->get_user_items( $filter, $total_rows );
465 $rows = is_array( $rows ) ? $rows : array();
466 $items = array();
467
468 foreach ( $rows as $row ) {
469 $enroll_user_id = absint( $row->user_id ?? 0 );
470 $enroll_course_id = absint( $row->item_id ?? 0 );
471 $course = $enroll_course_id > 0 ? CourseModel::find( $enroll_course_id, true ) : false;
472 $user = $enroll_user_id > 0 ? UserModel::find( $enroll_user_id, true ) : false;
473
474 $items[] = array(
475 'enrollment_id' => absint( $row->user_item_id ?? 0 ),
476 'user' => array(
477 'user_id' => $enroll_user_id,
478 'display_name' => $user instanceof UserModel ? $user->get_display_name() : '',
479 'email' => $user instanceof UserModel ? $user->get_email() : '',
480 ),
481 'course' => array(
482 'course_id' => $enroll_course_id,
483 'title' => $course instanceof CourseModel ? $course->get_title() : '',
484 ),
485 'status' => (string) ( $row->status ?? '' ),
486 'graduation' => (string) ( $row->graduation ?? '' ),
487 'start_time' => (string) ( $row->start_time ?? '' ),
488 'end_time' => (string) ( $row->end_time ?? '' ),
489 'ref_id' => absint( $row->ref_id ?? 0 ),
490 'ref_type' => (string) ( $row->ref_type ?? '' ),
491 'parent_id' => absint( $row->parent_id ?? 0 ),
492 );
493 }
494
495 return array(
496 'items' => $items,
497 'pagination' => array(
498 'page' => $page,
499 'per_page' => $per_page,
500 'total_items' => (int) $total_rows,
501 'total_pages' => self::total_pages( (int) $total_rows, $per_page ),
502 ),
503 );
504 } catch ( Throwable $e ) {
505 return self::internal();
506 }
507 }
508 }
509