PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.2
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 / user-item / class-lp-user-item-course.php

class-lp-user-item-course.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.2, at inc/user-item/class-lp-user-item-course.php

1,220 lines 28.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use LearnPress\Filters\UserItemsFilter;
4 use LearnPress\Models\UserItems\UserCourseModel;
5
6 /**
7 * Class LP_User_Item_Course
8 */
9
10 class LP_User_Item_Course extends LP_User_Item {
11 public $_item_type = LP_COURSE_CPT;
12 public $_ref_type = LP_ORDER_CPT;
13
14 /**
15 * Course
16 *
17 * @var LP_Course
18 */
19 protected $_course = 0;
20
21 /**
22 * @var LP_User
23 */
24 protected $_user = 0;
25
26 /**
27 * @var array
28 */
29 protected $_items_by_item_ids = array();
30
31 /**
32 * @var array
33 */
34 protected $_items_by_order = array();
35
36 /**
37 * @var bool
38 */
39 protected $_loaded = false;
40
41 /**
42 * @var LP_User_CURD
43 */
44 protected $_curd = null;
45
46 /**
47 * LP_User_Item_Course constructor.
48 *
49 * @param object|array $item
50 */
51 public function __construct( $item ) {
52 /**
53 * Todo: Fix for item is course_id, will be removed when change all to array or object
54 * @editor tungnx 4.1.6.9
55 */
56 if ( gettype( $item ) == 'integer' ) {
57 $item_id = $item;
58 $item = [];
59 $item['item_id'] = $item_id;
60 }
61
62 $item = (array) $item;
63 $item['item_type'] = $this->_item_type;
64 $item['ref_type'] = $this->_ref_type;
65
66 if ( isset( $item['item_id'] ) ) {
67 $this->_course = learn_press_get_course( $item['item_id'] );
68 }
69
70 parent::__construct( $item );
71
72 $this->_curd = new LP_User_CURD();
73 $this->_changes = array();
74 }
75
76 /*public function load() {
77 if ( ! $this->_loaded ) {
78 $this->read_items();
79
80 $this->_loaded = true;
81 }
82 }*/
83
84 /**
85 * Read items' data of course for the user.
86 *
87 * @param bool $refresh .
88 *
89 * @return array|bool
90 */
91 public function read_items( $refresh = false ) {
92 $user_course_item_id = $this->get_user_item_id();
93
94 if ( ! $this->_course || ( ! $user_course_item_id ) ) {
95 return false;
96 }
97
98 $course_items = $this->_course->get_item_ids();
99 $total_items = $this->_course->count_items();
100
101 if ( empty( $total_items ) ) {
102 return false;
103 }
104
105 $filter = new LP_User_Items_Filter();
106 $filter->parent_id = $user_course_item_id;
107 $filter->user_id = $this->get_user_id();
108 $user_course_items = LP_User_Items_DB::getInstance()->get_user_course_items( $filter );
109
110 if ( $user_course_items ) {
111 $tmp = array();
112 // Convert keys of array from numeric to keys is item id
113 foreach ( $user_course_items as $user_course_item ) {
114 $tmp[ $user_course_item->item_id ] = $user_course_item;
115 }
116
117 $user_course_items = $tmp;
118 unset( $tmp );
119 } else {
120 $user_course_items = array();
121 }
122
123 $items = array();
124
125 foreach ( $course_items as $item_id ) {
126 if ( ! empty( $user_course_items[ $item_id ] ) ) {
127 $user_course_item = (array) $user_course_items[ $item_id ];
128 } else {
129 $user_course_item = array(
130 'item_id' => $item_id,
131 'ref_id' => $this->get_id(),
132 'parent_id' => $user_course_item_id,
133 );
134 }
135
136 $course_item = apply_filters(
137 'learn-press/user-course-item',
138 LP_User_Item::get_item_object( $user_course_item ),
139 $user_course_item,
140 $this
141 );
142
143 if ( $course_item ) {
144 $items[ $item_id ] = $course_item;
145 }
146 }
147
148 return $items;
149 }
150
151 /**
152 * Get Id of course.
153 *
154 * @return int
155 * @since 3.3.0
156 */
157 public function get_course_id() {
158 return $this->get_data( 'item_id' );
159 }
160
161 /**
162 * @deprecated 4.1.7.3
163 */
164 public function get_finishing_type() {
165 _deprecated_function( __METHOD__, '4.1.7.3' );
166 /*$type = $this->get_meta( 'finishing_type' );
167
168 if ( ! $type ) {
169 $type = $this->is_exceeded() <= 0 ? 'exceeded' : 'click';
170
171 learn_press_update_user_item_meta( $this->get_user_item_id(), 'finishing_type', $type );
172
173 $this->set_meta( 'finishing_type', $type );
174 }
175
176 return $type;*/
177 }
178
179 public function offsetSet( $offset, $value ) {
180 // $this->set_data( $offset, $value );
181 // Do not allow to set value directly!
182 }
183
184 public function offsetUnset( $offset ) {
185 // Do not allow to unset value directly!
186 }
187
188 public function offsetGet( $offset ) {
189 $items = $this->read_items();
190
191 return $items && array_key_exists( $offset, $items ) ? $items[ $offset ] : false;
192 }
193
194 public function offsetExists( $offset ) {
195 $items = $this->read_items();
196
197 return array_key_exists( $offset, (array) $items );
198 }
199
200 /**
201 * @return LP_User_Item|bool
202 * @deprecated 4.1.6.9.2
203 */
204 /*public function get_viewing_item() {
205 $item = LP_Global::course_item();
206 if ( $item ) {
207 return $this[ $item->get_id() ];
208 }
209
210 return false;
211 }*/
212
213 /**
214 * Get course.
215 *
216 * @param string $return
217 *
218 * @return bool|LP_Course|int|mixed
219 */
220 public function get_course( string $return = '' ) {
221 $course_id = $this->get_data( 'item_id', 0 );
222 if ( $return == '' ) {
223 return $course_id ? learn_press_get_course( $course_id ) : false;
224 }
225
226 return $course_id;
227 }
228
229 /**
230 * Get result/processing course
231 *
232 * @param string $prop
233 *
234 * @return mixed
235 */
236 public function get_result( $prop = '' ) {
237 $results = $this->calculate_course_results();
238
239 // Fix temporary for case call 'grade' - addons called
240 if ( 'grade' === $prop ) {
241 if ( $results['pass'] ) {
242 $results['grade'] = 'passed';
243 } else {
244 $results['grade'] = 'failed';
245 }
246 }
247
248 return $prop && $results && array_key_exists( $prop, $results ) ? $results[ $prop ] : $results;
249 }
250
251 /**
252 * Get current progress of course for an user.
253 * Firstly, get data from cache if it is already loaded.
254 * If data is not loaded to cache then get from meta data.
255 * If meta data is not updated then calculate and update it
256 *
257 * @updated 3.1.0
258 *
259 * @param string $prop
260 *
261 * @return float|int
262 */
263 public function get_results( $prop = 'result' ) {
264 $course = $this->get_course();
265
266 if ( ! $course ) {
267 return false;
268 }
269
270 $results = LP_Object_Cache::get(
271 'course-' . $this->get_item_id() . '-' . $this->get_user_id(),
272 'course-results'
273 );
274
275 if ( $results === false ) {
276 $results = $this->calculate_course_results();
277
278 LP_Object_Cache::set(
279 'course-' . $this->get_item_id() . '-' . $this->get_user_id(),
280 $results,
281 'course-results'
282 );
283 }
284
285 return $prop && $results && array_key_exists( $prop, $results ) ? $results[ $prop ] : $results;
286 }
287
288 /**
289 * Calculate course result
290 */
291 public function calculate_course_results( bool $force_cache = false ) {
292 $results = array(
293 'count_items' => 0,
294 'completed_items' => 0,
295 'items' => array(),
296 'evaluate_type' => '',
297 'pass' => 0,
298 'result' => 0,
299 );
300
301 try {
302 $userCourseModel = UserCourseModel::find( $this->get_user_id(), $this->get_course_id(), true );
303 if ( ! $userCourseModel instanceof UserCourseModel ) {
304 return $results;
305 }
306
307 $results = $userCourseModel->calculate_course_results();
308 } catch ( Throwable $e ) {
309 LP_Debug::error_log( $e );
310 }
311
312 return $results;
313
314 // Code old
315 $items = array();
316 $results = array(
317 'count_items' => 0,
318 'completed_items' => 0,
319 'items' => array(),
320 'evaluate_type' => '',
321 'pass' => 0,
322 'result' => 0,
323 );
324
325 try {
326 $course = learn_press_get_course( $this->get_course_id() );
327
328 if ( ! $course ) {
329 throw new Exception( 'Course invalid!' );
330 }
331
332 $key_first_cache = 'calculate_course/' . $this->get_user_id() . '/' . $course->get_id();
333 $results_cache = LP_Cache::cache_load_first( 'get', $key_first_cache );
334 if ( false !== $results_cache && ! $force_cache ) {
335 return $results_cache;
336 }
337
338 $this->_course = $course;
339
340 if ( $this->is_finished() ) {
341 // Get result from lp_user_item_results
342 // Todo: tungnx - set cache
343 return LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() );
344 }
345
346 if ( $this->get_status() !== LP_COURSE_ENROLLED ) {
347 return $results;
348 }
349
350 $count_items = $course->count_items();
351 $count_items_completed = $this->count_items_completed();
352
353 $evaluate_type = get_post_meta( $course->get_id(), '_lp_course_result', true );
354 if ( ! $evaluate_type ) {
355 $evaluate_type = 'evaluate_lesson';
356 }
357 switch ( $evaluate_type ) {
358 case 'evaluate_lesson':
359 $results_evaluate = $this->evaluate_course_by_lesson( $count_items_completed, $course->count_items( LP_LESSON_CPT ) );
360 break;
361 case 'evaluate_final_quiz':
362 $results_evaluate = $this->evaluate_course_by_final_quiz();
363 break;
364 case 'evaluate_quiz':
365 $results_evaluate = $this->evaluate_course_by_quizzes_passed( $count_items_completed, $course->count_items( LP_QUIZ_CPT ) );
366 break;
367 case 'evaluate_questions':
368 case 'evaluate_mark':
369 $results_evaluate = $this->evaluate_course_by_question( $evaluate_type );
370 break;
371 default:
372 $results_evaluate = apply_filters( 'learn-press/evaluate_passed_conditions', $results, $evaluate_type, $this );
373 break;
374 }
375
376 if ( ! is_array( $results_evaluate ) ) {
377 $results_evaluate = array(
378 'result' => 0,
379 'pass' => 0,
380 );
381 }
382
383 $results_evaluate['result'] = round( $results_evaluate['result'], 2 );
384
385 $completed_items = intval( $count_items_completed->count_status ?? 0 );
386
387 $item_types = learn_press_get_course_item_types();
388 foreach ( $item_types as $item_type ) {
389 $item_type_key = str_replace( 'lp_', '', $item_type );
390
391 $items[ $item_type_key ] = array(
392 'completed' => $count_items_completed->{$item_type . '_status_completed'} ?? 0,
393 'passed' => $count_items_completed->{$item_type . '_graduation_passed'} ?? 0,
394 'total' => $course->count_items( $item_type ),
395 );
396 }
397
398 $results = array_merge(
399 $results_evaluate,
400 compact( 'count_items', 'completed_items', 'items', 'evaluate_type' )
401 );
402
403 $results = apply_filters(
404 'learn-press/update-course-results',
405 $results,
406 $this->get_item_id(),
407 $this->get_user_id(),
408 $this
409 );
410
411 LP_Cache::cache_load_first( 'set', $key_first_cache, $results );
412 } catch ( Throwable $e ) {
413 error_log( __METHOD__ . ': ' . $e->getMessage() );
414 }
415
416 return $results;
417 }
418
419 /**
420 * Get graduation
421 *
422 * @param string $context
423 *
424 * @return string
425 * @deprecated 4.1.6.9.3
426 */
427 public function get_grade( string $context = '' ): string {
428 $grade = $this->get_graduation() ?? '';
429
430 return $context == 'display' ? learn_press_course_grade_html( $grade, false ) : $grade;
431 }
432
433 /**
434 * @param int $decimal
435 *
436 * @return int|string
437 */
438 public function get_percent_result( $decimal = 1 ) {
439 return round( $this->get_result( 'result' ), $decimal );
440 }
441
442 /**
443 * Evaluate course result by lessons.
444 *
445 * @param $count_items_completed
446 * @param int $total_item_lesson
447 *
448 * @return array
449 * @author tungnx
450 * @since 4.1.4.1
451 * @version 1.0.0
452 */
453 protected function evaluate_course_by_lesson( $count_items_completed, int $total_item_lesson = 0 ): array {
454 $evaluate = array(
455 'result' => 0,
456 'pass' => 0,
457 );
458
459 $count_items_completed = intval( $count_items_completed->{LP_LESSON_CPT . '_status_completed'} ?? 0 );
460
461 if ( $total_item_lesson && $count_items_completed ) {
462 $evaluate['result'] = $count_items_completed * 100 / $total_item_lesson;
463 }
464
465 $passing_condition = $this->_course->get_passing_condition();
466 if ( $evaluate['result'] >= $passing_condition ) {
467 $evaluate['pass'] = 1;
468 }
469
470 return $evaluate;
471 }
472
473 /**
474 * Evaluate course result by final quiz.
475 *
476 * @return array
477 */
478 protected function evaluate_course_by_final_quiz(): array {
479 $lp_user_items_db = LP_User_Items_DB::getInstance();
480 $lp_user_item_result_db = LP_User_Items_Result_DB::instance();
481 $evaluate = array(
482 'result' => 0,
483 'pass' => 0,
484 );
485
486 try {
487 $quiz_final_id = get_post_meta( $this->get_course_id(), '_lp_final_quiz', true );
488
489 if ( ! $quiz_final_id ) {
490 throw new Exception( '' );
491 }
492
493 $quiz_final = learn_press_get_quiz( $quiz_final_id );
494
495 if ( ! $quiz_final ) {
496 throw new Exception( 'Quiz final invalid' );
497 }
498
499 $user_course = $this->get_last_user_course();
500
501 if ( ! $user_course ) {
502 throw new Exception( 'User course not exists' );
503 }
504
505 $filter = new LP_User_Items_Filter();
506 $filter->query_type = 'get_row';
507 $filter->parent_id = $user_course->user_item_id;
508 $filter->item_type = LP_QUIZ_CPT;
509 $filter->item_id = $quiz_final_id;
510 $user_quiz = $lp_user_items_db->get_user_course_items_by_item_type( $filter );
511
512 if ( ! $user_quiz ) {
513 throw new Exception();
514 }
515
516 // Get result did quiz
517 $quiz_result = $lp_user_item_result_db->get_result( $user_quiz->user_item_id );
518
519 if ( $quiz_result ) {
520 if ( ! isset( $quiz_result['result'] ) ) {
521 $evaluate['result'] = $quiz_result['user_mark'] * 100 / $quiz_result['mark'];
522 } else {
523 $evaluate['result'] = $quiz_result['result'];
524 }
525
526 $passing_condition = floatval( $quiz_final->get_data( 'passing_grade', 0 ) );
527 if ( $evaluate['result'] >= $passing_condition ) {
528 $evaluate['pass'] = 1;
529 }
530 }
531 } catch ( Throwable $e ) {
532
533 }
534
535 return $evaluate;
536 }
537
538 /**
539 * Evaluate course results by count quizzes passed/all quizzes.
540 *
541 * @author tungnx
542 * @since 4.1.4.1
543 * @version 1.0.0
544 */
545 protected function evaluate_course_by_quizzes_passed( $count_items_completed, $total_item_quizzes ): array {
546 $evaluate = array(
547 'result' => 0,
548 'pass' => 0,
549 );
550
551 $count_items_completed = intval( $count_items_completed->{LP_QUIZ_CPT . '_graduation_passed'} ?? 0 );
552
553 if ( $total_item_quizzes && $count_items_completed ) {
554 $evaluate['result'] = $count_items_completed * 100 / $total_item_quizzes;
555
556 $passing_condition = $this->_course->get_passing_condition();
557 if ( $evaluate['result'] >= $passing_condition ) {
558 $evaluate['pass'] = 1;
559 }
560 }
561
562 return $evaluate;
563 }
564
565 /**
566 * Evaluate course results by count questions true/all questions.
567 *
568 * @author tungnx
569 * @since 4.1.4.1
570 * @version 1.0.0
571 */
572 private function evaluate_course_by_questions( &$evaluate, $lp_quizzes, $total_questions ) {
573 $lp_user_item_results_db = LP_User_Items_Result_DB::instance();
574 $count_questions_correct = 0;
575
576 // get questions correct
577 foreach ( $lp_quizzes as $lp_quiz ) {
578 $lp_quiz_result = $lp_user_item_results_db->get_result( $lp_quiz->user_item_id );
579 if ( $lp_quiz_result ) {
580 $count_questions_correct += $lp_quiz_result['question_correct'];
581 }
582 }
583
584 if ( $total_questions && $count_questions_correct ) {
585 $evaluate['result'] = $count_questions_correct * 100 / $total_questions;
586
587 $passing_condition = $this->_course->get_passing_condition();
588 if ( $evaluate['result'] >= $passing_condition ) {
589 $evaluate['pass'] = 1;
590 }
591 }
592 }
593
594 /**
595 * Evaluate course results by total mark of questions.
596 *
597 * @author tungnx
598 * @since 4.1.4.1
599 * @version 1.0.0
600 */
601 private function evaluate_course_by_mark( &$evaluate, $lp_quizzes, $total_mark_questions ) {
602 $lp_user_item_results_db = LP_User_Items_Result_DB::instance();
603 $count_mark_questions_receiver = 0;
604
605 foreach ( $lp_quizzes as $lp_quiz ) {
606 $lp_quiz_result = $lp_user_item_results_db->get_result( $lp_quiz->user_item_id );
607 if ( $lp_quiz_result ) {
608 $count_mark_questions_receiver += $lp_quiz_result['user_mark'];
609 }
610 }
611
612 if ( $count_mark_questions_receiver && $total_mark_questions ) {
613 $evaluate['result'] = $count_mark_questions_receiver * 100 / $total_mark_questions;
614
615 $passing_condition = floatval( $this->_course->get_passing_condition() );
616 if ( $evaluate['result'] >= $passing_condition ) {
617 $evaluate['pass'] = 1;
618 }
619 }
620 }
621
622 /**
623 * Evaluate course results by total mark of questions.
624 *
625 * @author tungnx
626 * @since 4.1.4.1
627 * @version 1.0.0
628 */
629 protected function evaluate_course_by_question( string $evaluate_type ): array {
630 $lp_user_items_db = LP_User_Items_DB::getInstance();
631 $evaluate = array(
632 'result' => 0,
633 'pass' => 0,
634 );
635
636 try {
637 $user_course = $this->get_last_user_course();
638
639 if ( ! $user_course ) {
640 throw new Exception( 'User course not exists!' );
641 }
642
643 // get quiz_ids
644 $filter_get_quiz_ids = new LP_User_Items_Filter();
645 $filter_get_quiz_ids->parent_id = $user_course->user_item_id;
646 $filter_get_quiz_ids->item_type = LP_QUIZ_CPT;
647 $lp_quizzes = $lp_user_items_db->get_user_course_items_by_item_type( $filter_get_quiz_ids );
648
649 // Get total questions, mark
650 // Todo: Tungnx - save (questions, mark) total when save quiz, course, if not query again
651 $course = $this->_course;
652 if ( is_int( $course ) ) {
653 $course = learn_press_get_course( $course );
654 }
655
656 $total_questions = 0;
657 $total_mark_question = 0;
658
659 // Get all items' course
660 $sections_items = $course->get_full_sections_and_items_course();
661
662 foreach ( $sections_items as $section_items ) {
663 foreach ( $section_items->items as $item ) {
664 $itemObj = $course->get_item( $item->id );
665
666 if ( ! $itemObj instanceof LP_Course_Item ) {
667 continue;
668 }
669
670 if ( $item->type == LP_QUIZ_CPT ) {
671 $total_questions += count( $itemObj->get_question_ids() );
672 $total_mark_question += $itemObj->get_mark();
673 }
674 }
675 }
676 // End get total questions, mark
677
678 switch ( $evaluate_type ) {
679 case 'evaluate_questions':
680 $this->evaluate_course_by_questions( $evaluate, $lp_quizzes, $total_questions );
681 break;
682 case 'evaluate_mark':
683 $this->evaluate_course_by_mark( $evaluate, $lp_quizzes, $total_mark_question );
684 break;
685 default:
686 break;
687 }
688
689 // Get results of each quiz - has questions
690 } catch ( Throwable $e ) {
691 error_log( __FUNCTION__ . ': ' . $e->getMessage() );
692 }
693
694 return $evaluate;
695 }
696
697 /**
698 * Check course of use has enrolled
699 */
700 public function is_enrolled(): bool {
701 return $this->get_status() == LP_COURSE_ENROLLED;
702 }
703
704 /**
705 * Check course of use has purchased
706 * @author tungnx
707 * @since 4.1.3
708 * @version 1.0.0
709 */
710 public function is_purchased(): bool {
711 return $this->get_status() == LP_COURSE_PURCHASED;
712 }
713
714 public function get_level() {
715 if ( ! $this->is_exists() ) {
716 return 0;
717 }
718
719 $level = 10;
720
721 switch ( $this->get_status() ) {
722 case 'enrolled':
723 $level = 20;
724 break;
725 case 'finished':
726 $level = 30;
727 break;
728
729 }
730
731 return $level;
732 }
733
734 /**
735 * @deprecated 4.1.6.9.2
736 */
737 /*protected function _is_passed( $result ) {
738 $is_passed = LP_COURSE_GRADUATION_FAILED;
739 $result = round( $result, 2 );
740
741 if ( $result >= $this->get_passing_condition() ) {
742 $is_passed = LP_COURSE_GRADUATION_PASSED;
743 }
744
745 return apply_filters( 'learnpress/user/course/is-passed', $is_passed, $result );
746 }*/
747
748 /**
749 * Get completed items.
750 *
751 * @param string $type - Optional. Filter by type (such lp_quiz, lp_lesson) if passed
752 * @param bool $with_total - Optional. Include total if TRUE
753 * @param int $section_id - Optional. Get in specific section
754 *
755 * @return array|bool|mixed
756 * @editor tungnx
757 */
758 public function get_completed_items( $type = '', $with_total = false, $section_id = 0 ) {
759
760 $this->read_items();
761
762 // $completed_items = array(0,100);
763 // return $with_total ? $completed_items : $completed_items[0];
764
765 if ( ! $this->_course ) {
766 return 0;
767 }
768
769 $key = sprintf(
770 '%d-%d-%s',
771 $this->get_user_id(),
772 $this->_course->get_id(),
773 md5( build_query( func_get_args() ) )
774 );
775
776 $completed_items = LP_Object_Cache::get( $key, 'learn-press/user-completed-items' );
777
778 if ( false === $completed_items ) {
779 $completed = 0;
780 $total = 0;
781 $section_items = array();
782
783 if ( $section_id ) {
784 $section = $this->_course->get_sections( 'object', $section_id );
785
786 if ( $section ) {
787 $section_items = $section->get_items();
788
789 if ( $section_items ) {
790 $section_items = array_keys( $section_items );
791 }
792 }
793 }
794
795 $items = $this->get_items();
796 if ( $items ) {
797 foreach ( $items as $item ) {
798
799 if ( $section_id && ! in_array( $item->get_id(), $section_items ) ) {
800 continue;
801 }
802
803 if ( $type ) {
804 $item_type = $item->get_data( 'item_type' );
805 } else {
806 $item_type = '';
807 }
808
809 if ( $type === $item_type ) {
810 if ( $item->get_status() == 'completed' ) {
811 ++$completed;
812 }
813 $completed = apply_filters(
814 'learn-press/course-item/completed',
815 $completed,
816 $item,
817 $item->get_status()
818 );
819 // if ( ! $item->is_preview() ) {
820 ++$total;
821 // }
822 }
823 }
824 }
825 $completed_items = array( $completed, $total );
826 LP_Object_Cache::set( $key, $completed_items, 'learn-press/user-completed-items' );
827 }
828
829 return $with_total ? $completed_items : $completed_items[0];
830 }
831
832 /**
833 * Get completed items.
834 *
835 * @return object
836 * @editor tungnx
837 * @modify 4.1.4.1
838 * @since 4.0.0
839 * @version 4.0.1
840 */
841 public function count_items_completed() {
842 $lp_user_items_db = LP_User_Items_DB::getInstance();
843 $count_items_completed = new stdClass();
844
845 try {
846 $course = learn_press_get_course( $this->get_course_id() );
847 if ( ! $course ) {
848 throw new Exception( 'Course is invalid!' );
849 }
850
851 $user_course = $this->get_last_user_course();
852 if ( ! $user_course ) {
853 throw new Exception( 'User course is invalid!' );
854 }
855
856 $filter_count = new LP_User_Items_Filter();
857 $filter_count->parent_id = $user_course->user_item_id;
858 $filter_count->item_id = $this->get_course_id();
859 $filter_count->user_id = $this->get_user_id();
860 $filter_count->status = 'completed';
861 $filter_count->graduation = LP_COURSE_GRADUATION_PASSED;
862 $count_items_completed = $lp_user_items_db->count_items_of_course_with_status( $filter_count );
863 } catch ( Throwable $e ) {
864 error_log( __METHOD__ . ': ' . $e->getMessage() );
865 }
866
867 return $count_items_completed;
868 }
869
870 /**
871 * Get items completed by percentage.
872 *
873 * @param string $type - Optional. Filter by type or not
874 * @param int $section_id - Optional. Get in specific section
875 *
876 * @return float|int
877 */
878 public function get_percent_completed_items( $type = '', $section_id = 0 ) {
879 $values = $this->get_completed_items( $type, true, $section_id );
880 if ( $values[1] ) {
881 return $values[0] / $values[1] * 100;
882 }
883
884 return 0;
885 }
886
887 /**
888 * Get passing condition criteria.
889 *
890 * @return string
891 */
892 public function get_passing_condition() {
893 return $this->_course->get_passing_condition();
894 }
895
896 /**
897 * Get all items in course.
898 *
899 * @param bool $refresh
900 *
901 * @return LP_User_Item[]
902 */
903 public function get_items( $refresh = false ) {
904 return $this->read_items();
905
906 /*
907 return LP_Object_Cache::get(
908 $this->get_user_id() . '-' . $this->get_id(),
909 'learn-press/user-course-item-objects'
910 );*/
911 }
912
913 /**
914 * Check course is completed or not.
915 *
916 * @return bool
917 * @editor tungnx
918 * @modify 4.1.3
919 */
920 public function is_finished(): bool {
921 return $this->get_status() == LP_COURSE_FINISHED;
922 }
923
924 /**
925 * Check course graduation is passed or not.
926 *
927 * @return bool
928 */
929 public function is_graduated() {
930 return $this->get_graduation();
931 }
932
933 /**
934 * @return bool
935 * @deprecated 4.1.6.9.2
936 */
937 /*public function can_graduated() {
938 return $this->get_results( 'result' ) >= $this->get_passing_condition();
939 }*/
940
941 function __destruct() {
942 // TODO: Implement __unset() method.
943 }
944
945 /**
946 * Get item user attend on course by item_id.
947 *
948 * @param int $item_id
949 *
950 * @return bool|LP_User_Item
951 * @editor tungnx
952 * @modify 4.1.6.9
953 * @since 3.0.0
954 * @version 4.0.1
955 */
956 public function get_item( $item_id ) {
957 $item = false;
958
959 try {
960 $filter = new LP_User_Items_Filter();
961 $filter->item_id = $item_id;
962 $filter->ref_id = $this->get_course_id();
963 $filter->user_id = $this->get_user();
964 $item_result = LP_User_Items_DB::getInstance()->get_user_course_item( $filter );
965
966 if ( $item_result ) {
967 $item_result = (array) $item_result;
968 $item = LP_User_Item::get_item_object( $item_result );
969 }
970 } catch ( Throwable $e ) {
971 error_log( $e->getMessage() );
972 }
973
974 return $item;
975 }
976
977 /**
978 * @param $item_id
979 * @param string $prop
980 *
981 * @return bool|float|int
982 * @throws Exception
983 */
984 public function get_item_result( $item_id, $prop = 'result' ) {
985 $item = $this->get_item( $item_id );
986
987 if ( $item instanceof LP_User_Item_Quiz ) {
988 /**
989 * @var LP_User_Item_Quiz $item
990 */
991 return $item->get_graduation();
992 } elseif ( $item ) {
993 return $item->get_result( $prop );
994 }
995
996 return false;
997 }
998
999 /**
1000 * @param $id
1001 *
1002 * @return LP_User_Item_Quiz|bool
1003 */
1004 public function get_item_quiz( $id ) {
1005
1006 return $this->get_item( $id );
1007 }
1008
1009 /**
1010 * Get number of retaken times for user course.
1011 *
1012 * @return int
1013 */
1014 public function get_retaken_count(): int {
1015 return (int) ( learn_press_get_user_item_meta( $this->get_user_item_id(), '_lp_retaken_count' ) );
1016 }
1017
1018 /**
1019 * Increase retaken count.
1020 *
1021 * @return bool|int
1022 */
1023 public function increase_retake_count() {
1024 $count = $this->get_retaken_count();
1025 ++$count;
1026
1027 return $this->update_meta( '_lp_retaken_count', $count );
1028 }
1029
1030 /**
1031 * Update course item and it's child.
1032 *
1033 * @TODO: tungnx - review to modify
1034 * @deprecated 4.1.7.3
1035 */
1036 public function save() {
1037 _deprecated_function( __METHOD__, '4.1.7.3' );
1038 /**
1039 * @var LP_User_Item $item
1040 */
1041 $this->update();
1042 $items = $this->get_items();
1043 if ( ! $items ) {
1044 return false;
1045 }
1046
1047 foreach ( $items as $item_id => $item ) {
1048 if ( ! $item->get_status() ) {
1049 continue;
1050 }
1051
1052 /**
1053 * Autofill the end-time if it isn't already set
1054 */
1055 if ( in_array( $item->get_status(), array( 'completed', 'finished' ) ) ) {
1056 if ( ! $item->get_end_time() || $item->get_end_time()->is_null() ) {
1057 $item->set_end_time( current_time( 'mysql', 1 ) );
1058 }
1059 }
1060
1061 $item->update();
1062 }
1063
1064 return true;
1065 }
1066
1067 /**
1068 * Get passed items.
1069 *
1070 * @param string $type - Optional. Filter by type (such lp_quiz, lp_lesson) if passed
1071 * @param bool $with_total - Optional. Include total if TRUE
1072 * @param int $section_id - Optional. Get in specific section
1073 *
1074 * @return array|bool|mixed
1075 * @deprecated 4.1.6.9
1076 */
1077 public function get_passed_items( $type = '', $with_total = false, $section_id = 0 ) {
1078 _deprecated_function( __FUNCTION__, '4.1.6.9' );
1079 $this->read_items();
1080
1081 $key = sprintf(
1082 '%d-%d-%s',
1083 $this->get_user_id(),
1084 $this->_course->get_id(),
1085 md5( build_query( func_get_args() ) )
1086 );
1087 $passed_items = LP_Object_Cache::get( $key, 'learn-press/user-passed-items' );
1088
1089 if ( false === $passed_items ) {
1090 $passed = 0;
1091 $total = 0;
1092 $section_items = array();
1093
1094 $section = $this->_course->get_sections( 'object', $section_id );
1095
1096 if ( $section_id && $section ) {
1097 $section_items = $section->get_items();
1098
1099 if ( $section_items ) {
1100 $section_items = array_keys( $section_items );
1101 }
1102 }
1103
1104 $items = $this->get_items();
1105
1106 if ( $items ) {
1107 foreach ( $items as $item ) {
1108
1109 if ( $section_id && ! in_array( $item->get_id(), $section_items ) ) {
1110 continue;
1111 }
1112
1113 if ( $type ) {
1114 $item_type = $item->get_data( 'item_type' );
1115 } else {
1116 $item_type = '';
1117 }
1118
1119 if ( $type === $item_type ) {
1120 if ( $item->get_status( 'graduation' ) == 'passed' ) {
1121 ++$passed;
1122 }
1123 $passed = apply_filters(
1124 'learn-press/course-item/passed',
1125 $passed,
1126 $item,
1127 $item->get_status()
1128 );
1129 // if ( ! $item->is_preview() ) {
1130 ++$total;
1131 // }
1132 }
1133 }
1134 }
1135 $passed_items = array( $passed, $total );
1136 LP_Object_Cache::set( $key, $passed_items, 'learn-press/user-passed-items' );
1137 }
1138
1139 return $with_total ? $passed_items : $passed_items[0];
1140 }
1141
1142 /**
1143 * Get Order ID
1144 *
1145 * @return array|mixed
1146 * @since 4.1.3
1147 * @version 1.0.0
1148 * @author tungnx
1149 */
1150 public function get_order_id() {
1151 return $this->get_data( 'ref_id', 0 );
1152 }
1153
1154 /**
1155 * Get Order
1156 *
1157 * @throws Exception
1158 * @since 4.1.3
1159 * @version 1.0.0
1160 * @author tungnx
1161 */
1162 public function get_order() {
1163 $order = false;
1164
1165 if ( $this->get_order_id() ) {
1166 $order = new LP_Order( $this->get_order_id() );
1167 }
1168
1169 return $order;
1170 }
1171
1172 /**
1173 * Get child item ids by type item
1174 *
1175 * @return object|null
1176 */
1177 public function get_last_user_course() {
1178 $lp_user_items_db = LP_User_Items_DB::getInstance();
1179 $user_course = null;
1180
1181 try {
1182 $filter_user_course = new LP_User_Items_Filter();
1183 $filter_user_course->item_id = $this->get_course_id();
1184 $filter_user_course->user_id = $this->get_user_id();
1185 $user_course = $lp_user_items_db->get_last_user_course( $filter_user_course );
1186 } catch ( Throwable $e ) {
1187 error_log( __FUNCTION__ . ':' . $e->getMessage() );
1188 }
1189
1190 return $user_course;
1191 }
1192
1193 /**
1194 * Get courses only by course's user are learning
1195 *
1196 * @param LP_User_Items_Filter|UserItemsFilter $filter
1197 * @param int $total_rows return total row query
1198 *
1199 * @return array|null|int|string
1200 */
1201 public static function get_user_courses( $filter, int &$total_rows = 0 ) {
1202 try {
1203 /*$key_cache = md5( json_encode( $filter ) );
1204 $courses_cache = LP_Cache::instance()->get_cache( $key_cache );
1205
1206 if ( false !== $courses_cache ) {
1207 return $courses_cache;
1208 }*/
1209
1210 $courses = LP_User_Items_DB::getInstance()->get_user_courses( $filter, $total_rows );
1211 //LP_Cache::instance()->set_cache( $key_cache, $courses );
1212 } catch ( Throwable $e ) {
1213 $courses = null;
1214 error_log( __FUNCTION__ . ': ' . $e->getMessage() );
1215 }
1216
1217 return $courses;
1218 }
1219 }
1220