| @@ -32,8 +32,14 @@ | ||
| 32 | 32 | const STATUS_PRIVATE = 'private'; |
| 33 | 33 | const STATUS_FUTURE = 'future'; |
| 34 | 34 | |
| 35 | 35 | /** |
| 36 | + * Course completion modes | |
| 37 | + */ | |
| 38 | + const MODE_FLEXIBLE = 'flexible'; | |
| 39 | + const MODE_STRICT = 'strict'; | |
| 40 | + | |
| 41 | + /** | |
| 36 | 42 | * Course mapped with the product using this meta key |
| 37 | 43 | * |
| 38 | 44 | * @var string |
| 39 | 45 | */ |
| @@ -126,9 +132,9 @@ | ||
| 126 | 132 | * Get course count by instructor |
| 127 | 133 | * |
| 128 | 134 | * @since 1.0.0 |
| 129 | 135 | * |
| 130 | - * @param $instructor_id | |
| 136 | + * @param int $instructor_id instructor ID. | |
| 131 | 137 | * |
| 132 | 138 | * @return null|string |
| 133 | 139 | */ |
| 134 | 140 | public static function get_course_count_by_instructor( $instructor_id ) { |
| @@ -161,9 +167,9 @@ | ||
| 161 | 167 | * Get course by quiz |
| 162 | 168 | * |
| 163 | 169 | * @since 1.0.0 |
| 164 | 170 | * |
| 165 | - * @param $quiz_id quiz id. | |
| 171 | + * @param int $quiz_id quiz id. | |
| 166 | 172 | * |
| 167 | 173 | * @return array|bool|null|object|void |
| 168 | 174 | */ |
| 169 | 175 | public static function get_course_by_quiz( $quiz_id ) { |
| @@ -172,9 +178,9 @@ | ||
| 172 | 178 | |
| 173 | 179 | if ( $post ) { |
| 174 | 180 | $course = get_post( $post->post_parent ); |
| 175 | 181 | if ( $course ) { |
| 176 | - if ( $course->post_type !== tutor()->course_post_type ) { | |
| 182 | + if ( tutor()->course_post_type !== $course->post_type ) { | |
| 177 | 183 | $course = get_post( $course->post_parent ); |
| 178 | 184 | } |
| 179 | 185 | return $course; |
| 180 | 186 | } |
| @@ -187,13 +193,13 @@ | ||
| 187 | 193 | * Get courses by a instructor |
| 188 | 194 | * |
| 189 | 195 | * @since 1.0.0 |
| 190 | 196 | * |
| 191 | - * @param integer $instructor_id | |
| 192 | - * @param array|string $post_status | |
| 193 | - * @param integer $offset | |
| 194 | - * @param integer $limit | |
| 195 | - * @param boolean $count_only | |
| 197 | + * @param integer $instructor_id instructor id. | |
| 198 | + * @param array|string $post_status post status. | |
| 199 | + * @param integer $offset offset. | |
| 200 | + * @param integer $limit limit. | |
| 201 | + * @param boolean $count_only count or not. | |
| 196 | 202 | * |
| 197 | 203 | * @return array|null|object |
| 198 | 204 | */ |
| 199 | 205 | public static function get_courses_by_instructor( $instructor_id = 0, $post_status = array( 'publish' ), int $offset = 0, int $limit = PHP_INT_MAX, $count_only = false ) { |
| @@ -202,9 +208,9 @@ | ||
| 202 | 208 | $limit = sanitize_text_field( $limit ); |
| 203 | 209 | $instructor_id = tutils()->get_user_id( $instructor_id ); |
| 204 | 210 | $course_post_type = tutor()->course_post_type; |
| 205 | 211 | |
| 206 | - if ( empty( $post_status ) || $post_status == 'any' ) { | |
| 212 | + if ( empty( $post_status ) || 'any' == $post_status ) { | |
| 207 | 213 | $where_post_status = ''; |
| 208 | 214 | } else { |
| 209 | 215 | ! is_array( $post_status ) ? $post_status = array( $post_status ) : 0; |
| 210 | 216 | $statuses = "'" . implode( "','", $post_status ) . "'"; |
| @@ -213,8 +219,9 @@ | ||
| 213 | 219 | |
| 214 | 220 | $select_col = $count_only ? " COUNT(DISTINCT $wpdb->posts.ID) " : " $wpdb->posts.* "; |
| 215 | 221 | $limit_offset = $count_only ? '' : " LIMIT $offset, $limit "; |
| 216 | 222 | |
| 223 | + //phpcs:disable | |
| 217 | 224 | $query = $wpdb->prepare( |
| 218 | 225 | "SELECT $select_col |
| 219 | 226 | FROM $wpdb->posts |
| 220 | 227 | LEFT JOIN {$wpdb->usermeta} |
| @@ -230,9 +237,11 @@ | ||
| 230 | 237 | $course_post_type, |
| 231 | 238 | $instructor_id, |
| 232 | 239 | $instructor_id |
| 233 | 240 | ); |
| 241 | + //phpcs:enable | |
| 234 | 242 | |
| 243 | + //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared | |
| 235 | 244 | return $count_only ? $wpdb->get_var( $query ) : $wpdb->get_results( $query, OBJECT ); |
| 236 | 245 | } |
| 237 | 246 | |
| 238 | 247 | /** |
| @@ -301,9 +310,9 @@ | ||
| 301 | 310 | * Marking course completed at Comment. |
| 302 | 311 | */ |
| 303 | 312 | global $wpdb; |
| 304 | 313 | |
| 305 | - $date = date( 'Y-m-d H:i:s', tutor_time() ); | |
| 314 | + $date = date( 'Y-m-d H:i:s', tutor_time() ); //phpcs:ignore | |
| 306 | 315 | |
| 307 | 316 | // Making sure that, hash is unique. |
| 308 | 317 | do { |
| 309 | 318 | $hash = substr( md5( wp_generate_password( 32 ) . $date . $course_id . $user_id ), 0, 16 ); |
| @@ -578,6 +587,143 @@ | ||
| 578 | 587 | return $query->posts; |
| 579 | 588 | } |
| 580 | 589 | |
| 581 | 590 | return array(); |
| 591 | + } | |
| 592 | + | |
| 593 | + /** | |
| 594 | + * Check the course is completeable or not | |
| 595 | + * | |
| 596 | + * @since 2.4.0 | |
| 597 | + * | |
| 598 | + * @param int $course_id course id. | |
| 599 | + * @param int $user_id user id. | |
| 600 | + * | |
| 601 | + * @return boolean | |
| 602 | + */ | |
| 603 | + public static function can_complete_course( $course_id, $user_id ) { | |
| 604 | + | |
| 605 | + $mode = tutor_utils()->get_option( 'course_completion_process' ); | |
| 606 | + if ( self::MODE_FLEXIBLE === $mode ) { | |
| 607 | + return true; | |
| 608 | + } | |
| 609 | + | |
| 610 | + if ( self::MODE_STRICT === $mode ) { | |
| 611 | + $completed_lesson = tutor_utils()->get_completed_lesson_count_by_course( $course_id, $user_id ); | |
| 612 | + $lesson_count = tutor_utils()->get_lesson_count_by_course( $course_id, $user_id ); | |
| 613 | + | |
| 614 | + if ( $completed_lesson < $lesson_count ) { | |
| 615 | + return false; | |
| 616 | + } | |
| 617 | + | |
| 618 | + $quizzes = array(); | |
| 619 | + $assignments = array(); | |
| 620 | + | |
| 621 | + $course_contents = tutor_utils()->get_course_contents_by_id( $course_id ); | |
| 622 | + if ( tutor_utils()->count( $course_contents ) ) { | |
| 623 | + foreach ( $course_contents as $content ) { | |
| 624 | + if ( 'tutor_quiz' === $content->post_type ) { | |
| 625 | + $quizzes[] = $content; | |
| 626 | + } | |
| 627 | + if ( 'tutor_assignments' === $content->post_type ) { | |
| 628 | + $assignments[] = $content; | |
| 629 | + } | |
| 630 | + } | |
| 631 | + } | |
| 632 | + | |
| 633 | + foreach ( $quizzes as $row ) { | |
| 634 | + $result = QuizModel::get_quiz_result( $row->ID ); | |
| 635 | + if ( 'pass' !== $result ) { | |
| 636 | + return false; | |
| 637 | + } | |
| 638 | + } | |
| 639 | + | |
| 640 | + if ( tutor()->has_pro ) { | |
| 641 | + foreach ( $assignments as $row ) { | |
| 642 | + $result = \TUTOR_ASSIGNMENTS\Assignments::get_assignment_result( $row->ID, $user_id ); | |
| 643 | + if ( 'pass' !== $result ) { | |
| 644 | + return false; | |
| 645 | + } | |
| 646 | + } | |
| 647 | + } | |
| 648 | + | |
| 649 | + return true; | |
| 650 | + } | |
| 651 | + | |
| 652 | + return false; | |
| 653 | + | |
| 654 | + } | |
| 655 | + | |
| 656 | + /** | |
| 657 | + * Check a course can be auto complete by an enrolled student. | |
| 658 | + * | |
| 659 | + * @since 2.4.0 | |
| 660 | + * | |
| 661 | + * @param int $course_id course id. | |
| 662 | + * @param int $user_id user id. | |
| 663 | + * | |
| 664 | + * @return boolean | |
| 665 | + */ | |
| 666 | + public static function can_autocomplete_course( $course_id, $user_id ) { | |
| 667 | + $auto_course_complete_option = (bool) tutor_utils()->get_option( 'auto_course_complete_on_all_lesson_completion' ); | |
| 668 | + if ( ! $auto_course_complete_option ) { | |
| 669 | + return false; | |
| 670 | + } | |
| 671 | + | |
| 672 | + $is_course_completed = tutor_utils()->is_completed_course( $course_id, $user_id ); | |
| 673 | + if ( $is_course_completed ) { | |
| 674 | + return false; | |
| 675 | + } | |
| 676 | + | |
| 677 | + $course_stats = tutor_utils()->get_course_completed_percent( $course_id, $user_id, true ); | |
| 678 | + if ( $course_stats['total_count'] && $course_stats['completed_count'] === $course_stats['total_count'] ) { | |
| 679 | + return self::can_complete_course( $course_id, $user_id ); | |
| 680 | + } else { | |
| 681 | + return false; | |
| 682 | + } | |
| 683 | + } | |
| 684 | + | |
| 685 | + /** | |
| 686 | + * Get review progress link when course progress 100% and | |
| 687 | + * User has pending or fail quiz or assignment | |
| 688 | + * | |
| 689 | + * @since 2.4.0 | |
| 690 | + * | |
| 691 | + * @param int $course_id course id. | |
| 692 | + * @param int $user_id user id. | |
| 693 | + * | |
| 694 | + * @return string course content permalink. | |
| 695 | + */ | |
| 696 | + public static function get_review_progress_link( $course_id, $user_id ) { | |
| 697 | + $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $user_id, true ); | |
| 698 | + $completed_percent = (int) $course_progress['completed_percent']; | |
| 699 | + $course_contents = tutor_utils()->get_course_contents_by_id( $course_id ); | |
| 700 | + $permalink = ''; | |
| 701 | + | |
| 702 | + if ( tutor_utils()->count( $course_contents ) && 100 === $completed_percent ) { | |
| 703 | + foreach ( $course_contents as $content ) { | |
| 704 | + if ( 'tutor_quiz' === $content->post_type ) { | |
| 705 | + $result = QuizModel::get_quiz_result( $content->ID, $user_id ); | |
| 706 | + if ( 'pass' !== $result ) { | |
| 707 | + $permalink = get_the_permalink( $content->ID ); | |
| 708 | + break; | |
| 709 | + } | |
| 710 | + } | |
| 711 | + | |
| 712 | + if ( tutor()->has_pro && 'tutor_assignments' === $content->post_type ) { | |
| 713 | + $result = \TUTOR_ASSIGNMENTS\Assignments::get_assignment_result( $content->ID, $user_id ); | |
| 714 | + if ( 'pass' !== $result ) { | |
| 715 | + $permalink = get_the_permalink( $content->ID ); | |
| 716 | + break; | |
| 717 | + } | |
| 718 | + } | |
| 719 | + } | |
| 720 | + } | |
| 721 | + | |
| 722 | + // Fallback link. | |
| 723 | + if ( empty( $permalink ) ) { | |
| 724 | + $permalink = tutils()->get_course_first_lesson( $course_id ); | |
| 725 | + } | |
| 726 | + | |
| 727 | + return $permalink; | |
| 582 | 728 | } |
| 583 | 729 | } |