PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7.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.1.7.2, at inc/user-item/class-lp-user-item-course.php

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