| @@ -9,26 +9,20 @@ | ||
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | 11 | namespace TUTOR; |
| 12 | 12 | |
| 13 | -defined( 'ABSPATH' ) || exit; | |
| 13 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 14 | + exit; | |
| 15 | +} | |
| 14 | 16 | |
| 15 | -use Tutor\Components\Button; | |
| 16 | -use Tutor\Components\Constants\Size; | |
| 17 | -use Tutor\Components\Constants\Variant; | |
| 18 | -use Tutor\Components\Progress; | |
| 19 | -use Tutor\Ecommerce\Tax; | |
| 20 | -use Tutor\Models\QuizModel; | |
| 17 | +use stdClass; | |
| 18 | +use TUTOR\Input; | |
| 21 | 19 | use Tutor\Helpers\HttpHelper; |
| 22 | 20 | use Tutor\Models\CourseModel; |
| 23 | -use Tutor\Components\Tooltip; | |
| 24 | 21 | use Tutor\Ecommerce\Ecommerce; |
| 25 | -use Tutor\Helpers\DateTimeHelper; | |
| 26 | 22 | use Tutor\Traits\JsonResponse; |
| 27 | 23 | use Tutor\Helpers\ValidationHelper; |
| 28 | -use Tutor\Models\EnrollmentModel; | |
| 29 | -use Tutor\Options_V2; | |
| 30 | -use TUTOR_ASSIGNMENTS\Assignments; | |
| 24 | +use TutorPro\CourseBundle\Models\BundleModel; | |
| 31 | 25 | |
| 32 | 26 | /** |
| 33 | 27 | * Course Class |
| 34 | 28 | * |
| @@ -69,46 +63,10 @@ | ||
| 69 | 63 | const SELLING_OPTION_BOTH = 'both'; // onetime and subscription. |
| 70 | 64 | const SELLING_OPTION_MEMBERSHIP = 'membership'; |
| 71 | 65 | const SELLING_OPTION_ALL = 'all'; |
| 72 | 66 | |
| 73 | - /** | |
| 74 | - * Tax collection settings meta | |
| 75 | - * | |
| 76 | - * @since 3.7.0 | |
| 77 | - */ | |
| 78 | - const TAX_ON_SINGLE_META = 'tutor_tax_on_single'; | |
| 79 | - const TAX_ON_SUBSCRIPTION_META = 'tutor_tax_on_subscription'; | |
| 80 | 67 | |
| 81 | 68 | /** |
| 82 | - * Additional data meta | |
| 83 | - * | |
| 84 | - * @since 3.6.0 | |
| 85 | - */ | |
| 86 | - const COURSE_BENEFITS_META = '_tutor_course_benefits'; | |
| 87 | - const COURSE_REQUIREMENTS_META = '_tutor_course_requirements'; | |
| 88 | - const COURSE_TARGET_AUDIENCE_META = '_tutor_course_target_audience'; | |
| 89 | - const COURSE_MATERIAL_INCLUDE_META = '_tutor_course_material_includes'; | |
| 90 | - const COURSE_DURATION_META = '_course_duration'; | |
| 91 | - | |
| 92 | - /** | |
| 93 | - * Course settings meta | |
| 94 | - * | |
| 95 | - * @since 3.6.0 | |
| 96 | - */ | |
| 97 | - const COURSE_ENABLE_QA_META = '_tutor_enable_qa'; | |
| 98 | - const PUBLIC_COURSE_META = '_tutor_is_public_course'; | |
| 99 | - const COURSE_SETTINGS_META = '_tutor_course_settings'; | |
| 100 | - const COURSE_LEVEL_META = '_tutor_course_level'; | |
| 101 | - | |
| 102 | - /** | |
| 103 | - * Meta keys used to identify and store Tutor course order data. | |
| 104 | - * | |
| 105 | - * @since 4.0.0 | |
| 106 | - */ | |
| 107 | - const IS_TUTOR_ORDER_FOR_COURSE_META = '_is_tutor_order_for_course'; | |
| 108 | - const TUTOR_ORDER_FOR_COURSE_ID_META = '_tutor_order_for_course_id_'; | |
| 109 | - | |
| 110 | - /** | |
| 111 | 69 | * Additional course meta info |
| 112 | 70 | * |
| 113 | 71 | * @var array |
| 114 | 72 | */ |
| @@ -160,8 +118,15 @@ | ||
| 160 | 118 | */ |
| 161 | 119 | add_action( 'save_tutor_course', array( $this, 'attach_product_with_course' ), 10, 2 ); |
| 162 | 120 | |
| 163 | 121 | /** |
| 122 | + * Add course level to course settings | |
| 123 | + * | |
| 124 | + * @since v.1.4.1 | |
| 125 | + */ | |
| 126 | + add_filter( 'tutor_course_settings_tabs', array( $this, 'add_course_level_to_settings' ) ); | |
| 127 | + | |
| 128 | + /** | |
| 164 | 129 | * Enable Disable Course Details Page Feature |
| 165 | 130 | * |
| 166 | 131 | * @since v.1.4.8 |
| 167 | 132 | */ |
| @@ -252,9 +217,9 @@ | ||
| 252 | 217 | * @since 1.9.8 |
| 253 | 218 | */ |
| 254 | 219 | add_action( 'tutor_do_enroll_after_login_if_attempt', array( $this, 'enroll_after_login_if_attempt' ), 10, 2 ); |
| 255 | 220 | |
| 256 | - add_action( 'wp_ajax_tutor_update_course_content_order', array( $this, 'ajax_update_course_content_order' ) ); | |
| 221 | + add_action( 'wp_ajax_tutor_update_course_content_order', array( $this, 'tutor_update_course_content_order' ) ); | |
| 257 | 222 | |
| 258 | 223 | add_action( 'wp_ajax_tutor_get_wc_product', array( $this, 'get_wc_product' ) ); |
| 259 | 224 | add_action( 'wp_ajax_tutor_get_wc_products', array( $this, 'get_wc_products' ) ); |
| 260 | 225 | |
| @@ -302,39 +267,11 @@ | ||
| 302 | 267 | add_filter( 'tutor_user_list_args', array( $this, 'user_list_args_for_instructor' ) ); |
| 303 | 268 | |
| 304 | 269 | add_filter( 'template_include', array( $this, 'handle_password_protected' ) ); |
| 305 | 270 | add_action( 'login_form_postpass', array( $this, 'handle_password_submit' ) ); |
| 306 | - add_filter( 'post_password_required', array( $this, 'bypass_password_for_enrolled' ), 10, 2 ); | |
| 307 | - add_filter( 'the_preview', array( $this, 'handle_schedule_courses' ) ); | |
| 308 | - | |
| 309 | - add_action( 'tutor_course_action_btn', array( $this, 'render_course_action_btn' ) ); | |
| 310 | - add_action( 'wp_ajax_tutor_complete_course', array( $this, 'ajax_tutor_complete_course' ) ); | |
| 311 | 271 | } |
| 312 | 272 | |
| 313 | 273 | /** |
| 314 | - * Handle schedule courses preview for instructors. | |
| 315 | - * | |
| 316 | - * @since 3.5.0 | |
| 317 | - * | |
| 318 | - * @param \WP_Post $content the preview post content. | |
| 319 | - * | |
| 320 | - * @return \WP_Post | |
| 321 | - */ | |
| 322 | - public function handle_schedule_courses( $content ) { | |
| 323 | - global $wp_query; | |
| 324 | - $course_coming_soon_enabled = (int) get_post_meta( $content->ID, '_tutor_course_enable_coming_soon', true ); | |
| 325 | - $is_instructor = tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $content->ID, true ); | |
| 326 | - if ( ! CourseModel::get_post_types( $content ) || current_user_can( 'administrator' ) || $is_instructor || $course_coming_soon_enabled ) { | |
| 327 | - return $content; | |
| 328 | - } | |
| 329 | - | |
| 330 | - $wp_query->set_404(); | |
| 331 | - status_header( 404 ); | |
| 332 | - nocache_headers(); | |
| 333 | - return $content; | |
| 334 | - } | |
| 335 | - | |
| 336 | - /** | |
| 337 | 274 | * Handle password protected course and bundle form submission. |
| 338 | 275 | * |
| 339 | 276 | * @since 3.2.1 |
| 340 | 277 | * |
| @@ -350,31 +287,8 @@ | ||
| 350 | 287 | } |
| 351 | 288 | } |
| 352 | 289 | |
| 353 | 290 | /** |
| 354 | - * Bypass password protection for enrolled users. | |
| 355 | - * | |
| 356 | - * @since 4.0.0 | |
| 357 | - * | |
| 358 | - * @param bool $required Whether the password is required. | |
| 359 | - * @param WP_Post $post The post object. | |
| 360 | - * | |
| 361 | - * @return bool false if the current user is enrolled, original value otherwise. | |
| 362 | - */ | |
| 363 | - public function bypass_password_for_enrolled( $required, $post ) { | |
| 364 | - if ( ! $required ) { | |
| 365 | - return $required; | |
| 366 | - } | |
| 367 | - | |
| 368 | - $post_types = array( tutor()->course_post_type, tutor()->bundle_post_type ); | |
| 369 | - if ( in_array( $post->post_type, $post_types, true ) && EnrollmentModel::is_enrolled( $post->ID ) ) { | |
| 370 | - return false; | |
| 371 | - } | |
| 372 | - | |
| 373 | - return $required; | |
| 374 | - } | |
| 375 | - | |
| 376 | - /** | |
| 377 | 291 | * Handle password protected course/bundle. |
| 378 | 292 | * |
| 379 | 293 | * @since 3.0.0 |
| 380 | 294 | * |
| @@ -473,14 +387,12 @@ | ||
| 473 | 387 | * @return void |
| 474 | 388 | */ |
| 475 | 389 | public function validate_video_source( $params, &$errors ) { |
| 476 | 390 | if ( isset( $params['video'] ) ) { |
| 477 | - $video_source_type = isset( $params['video']['source'] ) ? $params['video']['source'] : ''; | |
| 478 | - if ( tutor_is_rest() ) { | |
| 479 | - $video_source_type = isset( $params['video']['source_type'] ) ? $params['video']['source_type'] : ''; | |
| 480 | - } | |
| 391 | + $video_source = isset( $params['video']['source'] ) ? $params['video']['source'] : ''; | |
| 392 | + $video_source_type = isset( $params['video']['source_type'] ) ? $params['video']['source_type'] : ''; | |
| 481 | 393 | |
| 482 | - if ( '' === $video_source_type ) { | |
| 394 | + if ( '' === $video_source ) { | |
| 483 | 395 | $errors['video_source'] = __( 'Video source is required', 'tutor' ); |
| 484 | 396 | } elseif ( ! $this->is_valid_video_source_type( $video_source_type ) ) { |
| 485 | 397 | $errors['video_source'] = __( 'Invalid video source', 'tutor' ); |
| 486 | 398 | } |
| @@ -548,34 +460,48 @@ | ||
| 548 | 460 | } |
| 549 | 461 | } |
| 550 | 462 | |
| 551 | 463 | /** |
| 552 | - * Setup course categories and tags. | |
| 464 | + * Setup course categories and tags | |
| 553 | 465 | * |
| 554 | 466 | * @since 3.0.0 |
| 555 | 467 | * |
| 556 | - * @param int $post_id Post ID. | |
| 557 | - * @param array $params Array of params. | |
| 468 | + * @param int $post_id post id. | |
| 469 | + * @param array $params array of params. | |
| 558 | 470 | * |
| 559 | 471 | * @return void |
| 560 | 472 | */ |
| 561 | 473 | public function setup_course_categories_tags( $post_id, $params ) { |
| 562 | 474 | if ( isset( $params['course_categories'] ) && is_array( $params['course_categories'] ) ) { |
| 563 | - $valid_category_ids = ValidationHelper::validate_term_ids( | |
| 564 | - $params['course_categories'], | |
| 565 | - CourseModel::COURSE_CATEGORY | |
| 566 | - ); | |
| 567 | - wp_set_object_terms( $post_id, $valid_category_ids, CourseModel::COURSE_CATEGORY ); | |
| 475 | + $category_names = array(); | |
| 476 | + | |
| 477 | + foreach ( $params['course_categories'] as $category_id ) { | |
| 478 | + $term = get_term( $category_id, CourseModel::COURSE_CATEGORY ); | |
| 479 | + | |
| 480 | + if ( ! is_wp_error( $term ) && $term ) { | |
| 481 | + $category_names[] = $term->name; | |
| 482 | + } | |
| 483 | + } | |
| 484 | + | |
| 485 | + // Set category names on the post. | |
| 486 | + wp_set_object_terms( $post_id, $category_names, CourseModel::COURSE_CATEGORY ); | |
| 568 | 487 | } else { |
| 569 | 488 | wp_set_object_terms( $post_id, array(), CourseModel::COURSE_CATEGORY ); |
| 570 | 489 | } |
| 571 | 490 | |
| 572 | 491 | if ( isset( $params['course_tags'] ) && is_array( $params['course_tags'] ) ) { |
| 573 | - $valid_tag_ids = ValidationHelper::validate_term_ids( | |
| 574 | - $params['course_tags'], | |
| 575 | - CourseModel::COURSE_TAG | |
| 576 | - ); | |
| 577 | - wp_set_object_terms( $post_id, $valid_tag_ids, CourseModel::COURSE_TAG ); | |
| 492 | + $tag_names = array(); | |
| 493 | + | |
| 494 | + foreach ( $params['course_tags'] as $tag_id ) { | |
| 495 | + $term = get_term( $tag_id, CourseModel::COURSE_TAG ); | |
| 496 | + | |
| 497 | + if ( ! is_wp_error( $term ) && $term ) { | |
| 498 | + $tag_names[] = $term->name; | |
| 499 | + } | |
| 500 | + } | |
| 501 | + | |
| 502 | + // Set tag names on the post. | |
| 503 | + wp_set_object_terms( $post_id, $tag_names, CourseModel::COURSE_TAG ); | |
| 578 | 504 | } else { |
| 579 | 505 | wp_set_object_terms( $post_id, array(), CourseModel::COURSE_TAG ); |
| 580 | 506 | } |
| 581 | 507 | } |
| @@ -789,8 +715,11 @@ | ||
| 789 | 715 | try { |
| 790 | 716 | if ( isset( $params['pricing']['type'] ) ) { |
| 791 | 717 | update_post_meta( $post_id, self::COURSE_PRICE_TYPE_META, $params['pricing']['type'] ); |
| 792 | 718 | } |
| 719 | + if ( isset( $params['pricing']['product_id'] ) ) { | |
| 720 | + update_post_meta( $post_id, '_tutor_course_product_id', $params['pricing']['product_id'] ); | |
| 721 | + } | |
| 793 | 722 | } catch ( \Throwable $th ) { |
| 794 | 723 | throw new \Exception( $th->getMessage() ); |
| 795 | 724 | } |
| 796 | 725 | } |
| @@ -798,21 +727,8 @@ | ||
| 798 | 727 | update_post_meta( $post_id, '_tutor_enable_qa', $params['enable_qna'] ?? 'yes' ); |
| 799 | 728 | update_post_meta( $post_id, '_tutor_is_public_course', $params['is_public_course'] ?? 'no' ); |
| 800 | 729 | update_post_meta( $post_id, '_tutor_course_level', $params['course_level'] ); |
| 801 | 730 | |
| 802 | - /** | |
| 803 | - * Save tax collection settings | |
| 804 | - * | |
| 805 | - * @since 3.7.0 | |
| 806 | - */ | |
| 807 | - if ( isset( $params['tax_on_single'] ) ) { | |
| 808 | - update_post_meta( $post_id, self::TAX_ON_SINGLE_META, $params['tax_on_single'] ); | |
| 809 | - } | |
| 810 | - | |
| 811 | - if ( isset( $params['tax_on_subscription'] ) ) { | |
| 812 | - update_post_meta( $post_id, self::TAX_ON_SUBSCRIPTION_META, $params['tax_on_subscription'] ); | |
| 813 | - } | |
| 814 | - | |
| 815 | 731 | do_action( 'tutor_after_prepare_update_post_meta', $post_id, $params ); |
| 816 | 732 | } |
| 817 | 733 | |
| 818 | 734 | /** |
| @@ -912,9 +828,9 @@ | ||
| 912 | 828 | |
| 913 | 829 | update_post_meta( $course_id, self::COURSE_PRICE_TYPE_META, self::PRICE_TYPE_FREE ); |
| 914 | 830 | |
| 915 | 831 | $link = admin_url( 'admin.php?page=create-course' ); |
| 916 | - if ( tutor()->has_pro && Input::post( 'from_dashboard', false, Input::TYPE_BOOL ) ) { | |
| 832 | + if ( Input::post( 'from_dashboard', false, Input::TYPE_BOOL ) ) { | |
| 917 | 833 | $link = tutor_utils()->tutor_dashboard_url( 'create-course' ); |
| 918 | 834 | } |
| 919 | 835 | |
| 920 | 836 | $link = add_query_arg( array( 'course_id' => $course_id ), $link ); |
| @@ -932,20 +848,20 @@ | ||
| 932 | 848 | * Get course list |
| 933 | 849 | * |
| 934 | 850 | * @since 3.0.0 |
| 935 | 851 | * |
| 936 | - * @since 3.2.0 Refactor the arguments & response as per new design. | |
| 852 | + * @since 3.2.0 | |
| 937 | 853 | * |
| 854 | + * Refactor the arguments & response as per new design | |
| 855 | + * | |
| 938 | 856 | * @return void |
| 939 | 857 | */ |
| 940 | 858 | public function ajax_course_list() { |
| 941 | - tutor_utils()->check_nonce(); | |
| 942 | 859 | $this->check_access(); |
| 943 | 860 | |
| 944 | 861 | $limit = Input::post( 'limit', 10, Input::TYPE_INT ); |
| 945 | 862 | $offset = Input::post( 'offset', 0, Input::TYPE_INT ); |
| 946 | 863 | $search_term = ''; |
| 947 | - $post_status = Input::post( 'post_status', null ); | |
| 948 | 864 | |
| 949 | 865 | $filter = json_decode( wp_unslash( $_POST['filter'] ) ); //phpcs:ignore --sanitized already |
| 950 | 866 | if ( ! empty( $filter ) && property_exists( $filter, 'search' ) ) { |
| 951 | 867 | $search_term = Input::sanitize( $filter->search ); |
| @@ -951,9 +867,8 @@ | ||
| 951 | 867 | $search_term = Input::sanitize( $filter->search ); |
| 952 | 868 | } |
| 953 | 869 | |
| 954 | 870 | $args = array( |
| 955 | - 'post_status' => is_null( $post_status ) ? 'publish' : $post_status, | |
| 956 | 871 | 'posts_per_page' => $limit, |
| 957 | 872 | 'offset' => $offset, |
| 958 | 873 | 's' => $search_term, |
| 959 | 874 | ); |
| @@ -959,9 +874,14 @@ | ||
| 959 | 874 | ); |
| 960 | 875 | |
| 961 | 876 | $exclude = Input::post( 'exclude', array(), Input::TYPE_ARRAY ); |
| 962 | 877 | if ( count( $exclude ) ) { |
| 963 | - $exclude = array_filter( $exclude, fn( $id ) => is_numeric( $id ) && $id > 0 ); | |
| 878 | + $exclude = array_filter( | |
| 879 | + $exclude, | |
| 880 | + function ( $id ) { | |
| 881 | + return is_numeric( $id ); | |
| 882 | + } | |
| 883 | + ); | |
| 964 | 884 | $args['post__not_in'] = $exclude; |
| 965 | 885 | } |
| 966 | 886 | |
| 967 | 887 | $courses = CourseModel::get_courses_by_args( $args ); |
| @@ -1101,12 +1021,8 @@ | ||
| 1101 | 1021 | ) |
| 1102 | 1022 | ); |
| 1103 | 1023 | |
| 1104 | 1024 | $course_id = (int) $params['course_id']; |
| 1105 | - if ( ! $course_id ) { | |
| 1106 | - $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); | |
| 1107 | - } | |
| 1108 | - | |
| 1109 | 1025 | $this->check_access( $course_id ); |
| 1110 | 1026 | |
| 1111 | 1027 | $errors = array(); |
| 1112 | 1028 | $validation = $this->validate_inputs( $params ); |
| @@ -1195,32 +1111,15 @@ | ||
| 1195 | 1111 | public function ajax_unlink_page_builder() { |
| 1196 | 1112 | tutor_utils()->check_nonce(); |
| 1197 | 1113 | |
| 1198 | 1114 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1199 | - if ( ! $course_id ) { | |
| 1200 | - $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); | |
| 1201 | - } | |
| 1202 | - | |
| 1115 | + $builder = Input::post( 'builder' ); | |
| 1203 | 1116 | $this->check_access( $course_id ); |
| 1204 | 1117 | |
| 1205 | - $builder = Input::post( 'builder' ); | |
| 1206 | 1118 | if ( 'elementor' === $builder ) { |
| 1207 | 1119 | delete_post_meta( $course_id, '_elementor_edit_mode' ); |
| 1208 | 1120 | } elseif ( 'droip' === $builder ) { |
| 1209 | 1121 | delete_post_meta( $course_id, 'droip_editor_mode' ); |
| 1210 | - } elseif ( 'divi' === $builder ) { | |
| 1211 | - $old_post_content = get_post_meta( $course_id, '_et_pb_old_content', true ); | |
| 1212 | - $course = get_post( $course_id ); | |
| 1213 | - $course->post_content = $old_post_content; | |
| 1214 | - $result = wp_update_post( $course ); | |
| 1215 | - | |
| 1216 | - if ( $result && ! is_wp_error( $result ) ) { | |
| 1217 | - update_post_meta( $course_id, '_et_pb_use_builder', 'off' ); | |
| 1218 | - update_post_meta( $course_id, '_et_pb_old_content', '' ); | |
| 1219 | - delete_post_meta( $course_id, '_et_dynamic_cached_shortcodes' ); | |
| 1220 | - delete_post_meta( $course_id, '_et_dynamic_cached_attributes' ); | |
| 1221 | - delete_post_meta( $course_id, '_et_builder_module_features_cache' ); | |
| 1222 | - } | |
| 1223 | 1122 | } |
| 1224 | 1123 | |
| 1225 | 1124 | $this->json_response( |
| 1226 | 1125 | __( 'Builder unlinked successfully.', 'tutor' ), |
| @@ -1276,23 +1175,20 @@ | ||
| 1276 | 1175 | /** |
| 1277 | 1176 | * Get course contents |
| 1278 | 1177 | * |
| 1279 | 1178 | * @since 3.0.0 |
| 1280 | - * | |
| 1281 | - * @return void | |
| 1282 | 1179 | */ |
| 1283 | 1180 | public function ajax_course_contents() { |
| 1284 | 1181 | tutor_utils()->check_nonce(); |
| 1285 | 1182 | |
| 1286 | - $errors = array(); | |
| 1287 | 1183 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1288 | 1184 | |
| 1289 | - if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1185 | + $this->check_access( $course_id ); | |
| 1186 | + | |
| 1187 | + if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1290 | 1188 | $errors['course_id'] = __( 'Invalid course id', 'tutor' ); |
| 1291 | 1189 | } |
| 1292 | 1190 | |
| 1293 | - $this->check_access( $course_id ); | |
| 1294 | - | |
| 1295 | 1191 | if ( ! empty( $errors ) ) { |
| 1296 | 1192 | $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); |
| 1297 | 1193 | } |
| 1298 | 1194 | |
| @@ -1316,14 +1212,14 @@ | ||
| 1316 | 1212 | |
| 1317 | 1213 | $errors = array(); |
| 1318 | 1214 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1319 | 1215 | |
| 1320 | - if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1216 | + $this->check_access( $course_id ); | |
| 1217 | + | |
| 1218 | + if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1321 | 1219 | $errors['course_id'] = __( 'Invalid course id', 'tutor' ); |
| 1322 | 1220 | } |
| 1323 | 1221 | |
| 1324 | - $this->check_access( $course_id ); | |
| 1325 | - | |
| 1326 | 1222 | if ( ! empty( $errors ) ) { |
| 1327 | 1223 | $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); |
| 1328 | 1224 | } |
| 1329 | 1225 | |
| @@ -1334,9 +1230,9 @@ | ||
| 1334 | 1230 | $price = 0; |
| 1335 | 1231 | $sale_price = 0; |
| 1336 | 1232 | $product_id = tutor_utils()->get_course_product_id( $course_id ); |
| 1337 | 1233 | |
| 1338 | - if ( WooCommerce::MONETIZE_BY === $monetize_by ) { | |
| 1234 | + if ( 'wc' === $monetize_by ) { | |
| 1339 | 1235 | $product = wc_get_product( $product_id ); |
| 1340 | 1236 | if ( $product ) { |
| 1341 | 1237 | $product_name = $product->get_name(); |
| 1342 | 1238 | $price = $product->get_regular_price(); |
| @@ -1343,9 +1239,9 @@ | ||
| 1343 | 1239 | $sale_price = $product->get_sale_price(); |
| 1344 | 1240 | } |
| 1345 | 1241 | } |
| 1346 | 1242 | |
| 1347 | - if ( Ecommerce::MONETIZE_BY === $monetize_by ) { | |
| 1243 | + if ( 'tutor' === $monetize_by ) { | |
| 1348 | 1244 | $price = get_post_meta( $course_id, self::COURSE_PRICE_META, true ); |
| 1349 | 1245 | $sale_price = get_post_meta( $course_id, self::COURSE_SALE_PRICE_META, true ); |
| 1350 | 1246 | } |
| 1351 | 1247 | |
| @@ -1404,16 +1300,8 @@ | ||
| 1404 | 1300 | 'certificate' => false, |
| 1405 | 1301 | ), |
| 1406 | 1302 | ); |
| 1407 | 1303 | |
| 1408 | - $tax_on_single = get_post_meta( $course_id, self::TAX_ON_SINGLE_META, true ); | |
| 1409 | - $tax_on_subscription = get_post_meta( $course_id, self::TAX_ON_SUBSCRIPTION_META, true ); | |
| 1410 | - | |
| 1411 | - $data['tax_collection'] = array( | |
| 1412 | - 'tax_on_single' => '' === $tax_on_single ? '1' : $tax_on_single, | |
| 1413 | - 'tax_on_subscription' => '' === $tax_on_subscription ? '1' : $tax_on_subscription, | |
| 1414 | - ); | |
| 1415 | - | |
| 1416 | 1304 | $data = apply_filters( 'tutor_course_details_response', array_merge( $course, $data ) ); |
| 1417 | 1305 | |
| 1418 | 1306 | $this->json_response( __( 'Data retrieved successfully!', 'tutor' ), $data ); |
| 1419 | 1307 | } |
| @@ -1478,10 +1366,9 @@ | ||
| 1478 | 1366 | wp_enqueue_script( 'mce-view' ); |
| 1479 | 1367 | wp_enqueue_editor(); |
| 1480 | 1368 | |
| 1481 | 1369 | wp_enqueue_media(); |
| 1482 | - wp_enqueue_script( 'tutor-course-builder', tutor()->url . 'assets/js/tutor-course-builder.js', array( 'wp-date', 'wp-i18n', 'wp-element', 'wp-api' ), TUTOR_VERSION, true ); | |
| 1483 | - wp_set_script_translations( 'tutor-course-builder', 'tutor', tutor()->path . 'languages/' ); | |
| 1370 | + wp_enqueue_script( 'tutor-course-builder', tutor()->url . 'assets/js/tutor-course-builder.min.js', array( 'wp-date', 'wp-i18n', 'wp-element', 'wp-api' ), TUTOR_VERSION, true ); | |
| 1484 | 1371 | |
| 1485 | 1372 | wp_localize_script( |
| 1486 | 1373 | 'mce-view', |
| 1487 | 1374 | 'mceViewL10n', |
| @@ -1527,9 +1414,8 @@ | ||
| 1527 | 1414 | /** |
| 1528 | 1415 | * Localized only options to protect sensitive info like API keys. |
| 1529 | 1416 | */ |
| 1530 | 1417 | $required_options = array( |
| 1531 | - 'learning_mode', | |
| 1532 | 1418 | 'monetize_by', |
| 1533 | 1419 | 'enable_course_marketplace', |
| 1534 | 1420 | 'course_permalink_base', |
| 1535 | 1421 | 'supported_video_sources', |
| @@ -1549,12 +1435,8 @@ | ||
| 1549 | 1435 | $settings['course_builder_logo_url'] = wp_get_attachment_image_url( $full_settings['tutor_frontend_course_page_logo_id'] ?? 0, 'full' ); |
| 1550 | 1436 | $settings['chatgpt_key_exist'] = tutor()->has_pro && ! empty( $full_settings['chatgpt_api_key'] ?? '' ); |
| 1551 | 1437 | $settings['youtube_api_key_exist'] = ! empty( $full_settings['lesson_video_duration_youtube_api_key'] ?? '' ); |
| 1552 | 1438 | |
| 1553 | - $settings['enable_tax'] = Tax::get_setting( 'enable_tax', true ); | |
| 1554 | - $settings['is_tax_included_in_price'] = Tax::is_tax_included_in_price(); | |
| 1555 | - $settings['enable_individual_tax_control'] = Tax::get_setting( 'enable_individual_tax_control' ); | |
| 1556 | - | |
| 1557 | 1439 | $new_data = array( 'settings' => $settings ); |
| 1558 | 1440 | |
| 1559 | 1441 | $data = array_merge( $default_data, $new_data ); |
| 1560 | 1442 | |
| @@ -1600,15 +1482,10 @@ | ||
| 1600 | 1482 | $data['timezones'] = tutor_global_timezone_lists(); |
| 1601 | 1483 | $data['difficulty_levels'] = $difficulty_levels; |
| 1602 | 1484 | $data['supported_video_sources'] = $supported_video_sources; |
| 1603 | 1485 | $data['wp_rest_nonce'] = wp_create_nonce( 'wp_rest' ); |
| 1486 | + $data['max_upload_size'] = size_format( wp_max_upload_size() ); | |
| 1604 | 1487 | |
| 1605 | - if ( 'en_US' !== $data['local'] ) { | |
| 1606 | - $data['course_builder_basic_locales'] = tutils()->get_script_locale_data( 'tutor-course-builder-basic', $data['local'] ); | |
| 1607 | - $data['course_builder_curriculum_locales'] = tutils()->get_script_locale_data( 'tutor-course-builder-curriculum', $data['local'] ); | |
| 1608 | - $data['course_builder_additional_locales'] = tutils()->get_script_locale_data( 'tutor-course-builder-additional', $data['local'] ); | |
| 1609 | - } | |
| 1610 | - | |
| 1611 | 1488 | $data = apply_filters( 'tutor_course_builder_localized_data', $data ); |
| 1612 | 1489 | |
| 1613 | 1490 | return $data; |
| 1614 | 1491 | } |
| @@ -1731,38 +1608,23 @@ | ||
| 1731 | 1608 | /** |
| 1732 | 1609 | * Update course content order |
| 1733 | 1610 | * |
| 1734 | 1611 | * @since 1.0.0 |
| 1735 | - * @since 3.9.9 Check if user can manage course before updating order. | |
| 1736 | - * | |
| 1737 | 1612 | * @return void |
| 1738 | 1613 | */ |
| 1739 | - public function ajax_update_course_content_order() { | |
| 1614 | + public function tutor_update_course_content_order() { | |
| 1740 | 1615 | tutor_utils()->checking_nonce(); |
| 1741 | 1616 | |
| 1742 | - $sorting_order = Input::post( 'tutor_topics_lessons_sorting', '' ); | |
| 1743 | - $sorting_order = json_decode( $sorting_order, true ) ?? array(); | |
| 1744 | - | |
| 1745 | - if ( ! tutor_utils()->count( $sorting_order ) ) { | |
| 1746 | - wp_send_json_error( __( 'Sorting order is required', 'tutor' ) ); | |
| 1747 | - } | |
| 1748 | - | |
| 1749 | - $topic_id = (int) isset( $sorting_order[0], $sorting_order[0]['topic_id'] ) ? $sorting_order[0]['topic_id'] : 0; | |
| 1750 | - $course_id = wp_get_post_parent_id( $topic_id ); | |
| 1751 | - | |
| 1752 | - if ( ! $topic_id || ! $course_id ) { | |
| 1753 | - wp_send_json_error( tutor_utils()->error_message( 'invalid_req' ) ); | |
| 1754 | - } | |
| 1755 | - | |
| 1756 | - if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) || ! User::is_admin() ) { | |
| 1757 | - wp_send_json_error( tutor_utils()->error_message() ); | |
| 1758 | - } | |
| 1759 | - | |
| 1760 | 1617 | if ( Input::has( 'content_parent' ) ) { |
| 1761 | 1618 | $content_parent = Input::post( 'content_parent', array(), Input::TYPE_ARRAY ); |
| 1762 | 1619 | $topic_id = tutor_utils()->array_get( 'parent_topic_id', $content_parent ); |
| 1763 | 1620 | $content_id = tutor_utils()->array_get( 'content_id', $content_parent ); |
| 1764 | 1621 | |
| 1622 | + if ( ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) { | |
| 1623 | + wp_send_json_success( array( 'message' => __( 'Access Denied!', 'tutor' ) ) ); | |
| 1624 | + exit; | |
| 1625 | + } | |
| 1626 | + | |
| 1765 | 1627 | // Update the parent topic id of the content. |
| 1766 | 1628 | global $wpdb; |
| 1767 | 1629 | $wpdb->update( $wpdb->posts, array( 'post_parent' => $topic_id ), array( 'ID' => $content_id ) ); |
| 1768 | 1630 | } |
| @@ -1767,9 +1629,9 @@ | ||
| 1767 | 1629 | $wpdb->update( $wpdb->posts, array( 'post_parent' => $topic_id ), array( 'ID' => $content_id ) ); |
| 1768 | 1630 | } |
| 1769 | 1631 | |
| 1770 | 1632 | // Save course content order. |
| 1771 | - $this->save_course_content_order( $sorting_order ); | |
| 1633 | + $this->save_course_content_order(); | |
| 1772 | 1634 | |
| 1773 | 1635 | wp_send_json_success(); |
| 1774 | 1636 | } |
| 1775 | 1637 | |
| @@ -1796,11 +1658,9 @@ | ||
| 1796 | 1658 | /** |
| 1797 | 1659 | * Restrict media |
| 1798 | 1660 | * |
| 1799 | 1661 | * @since 1.0.0 |
| 1800 | - * | |
| 1801 | 1662 | * @param string $where where clause. |
| 1802 | - * | |
| 1803 | 1663 | * @return string |
| 1804 | 1664 | */ |
| 1805 | 1665 | public function restrict_media( $where ) { |
| 1806 | 1666 | $action = Input::post( 'action' ); |
| @@ -1816,58 +1676,58 @@ | ||
| 1816 | 1676 | /** |
| 1817 | 1677 | * Save course content order |
| 1818 | 1678 | * |
| 1819 | 1679 | * @since 1.0.0 |
| 1820 | - * @since 3.9.8 param $order added. | |
| 1821 | - * | |
| 1822 | - * @param array $sort_order the lesson and topic order array. | |
| 1823 | - * | |
| 1824 | 1680 | * @return void |
| 1825 | 1681 | */ |
| 1826 | - private function save_course_content_order( $sort_order = array() ) { | |
| 1682 | + private function save_course_content_order() { | |
| 1827 | 1683 | global $wpdb; |
| 1828 | 1684 | |
| 1829 | - if ( ! tutor_utils()->count( $sort_order ) ) { | |
| 1830 | - return; | |
| 1831 | - } | |
| 1685 | + $new_order = Input::post( 'tutor_topics_lessons_sorting' ); | |
| 1686 | + if ( ! empty( $new_order ) ) { | |
| 1687 | + $order = json_decode( $new_order, true ); | |
| 1832 | 1688 | |
| 1833 | - $i = 0; | |
| 1834 | - foreach ( $sort_order as $topic ) { | |
| 1835 | - ++$i; | |
| 1836 | - $wpdb->update( | |
| 1837 | - $wpdb->posts, | |
| 1838 | - array( 'menu_order' => $i ), | |
| 1839 | - array( 'ID' => $topic['topic_id'] ) | |
| 1840 | - ); | |
| 1689 | + if ( is_array( $order ) && count( $order ) ) { | |
| 1690 | + $i = 0; | |
| 1691 | + foreach ( $order as $topic ) { | |
| 1692 | + $i++; | |
| 1693 | + $wpdb->update( | |
| 1694 | + $wpdb->posts, | |
| 1695 | + array( 'menu_order' => $i ), | |
| 1696 | + array( 'ID' => $topic['topic_id'] ) | |
| 1697 | + ); | |
| 1841 | 1698 | |
| 1842 | - /** | |
| 1843 | - * Removing All lesson with topic | |
| 1844 | - */ | |
| 1845 | - $wpdb->update( | |
| 1846 | - $wpdb->posts, | |
| 1847 | - array( 'post_parent' => 0 ), | |
| 1848 | - array( 'post_parent' => $topic['topic_id'] ) | |
| 1849 | - ); | |
| 1699 | + /** | |
| 1700 | + * Removing All lesson with topic | |
| 1701 | + */ | |
| 1850 | 1702 | |
| 1851 | - /** | |
| 1852 | - * Lesson Attaching with topic ID | |
| 1853 | - * Sorting lesson | |
| 1854 | - */ | |
| 1855 | - if ( isset( $topic['lesson_ids'] ) ) { | |
| 1856 | - $lesson_ids = $topic['lesson_ids']; | |
| 1857 | - } else { | |
| 1858 | - $lesson_ids = array(); | |
| 1859 | - } | |
| 1860 | - if ( count( $lesson_ids ) ) { | |
| 1861 | - foreach ( $lesson_ids as $lesson_key => $lesson_id ) { | |
| 1862 | 1703 | $wpdb->update( |
| 1863 | 1704 | $wpdb->posts, |
| 1864 | - array( | |
| 1865 | - 'post_parent' => $topic['topic_id'], | |
| 1866 | - 'menu_order' => $lesson_key, | |
| 1867 | - ), | |
| 1868 | - array( 'ID' => $lesson_id ) | |
| 1705 | + array( 'post_parent' => 0 ), | |
| 1706 | + array( 'post_parent' => $topic['topic_id'] ) | |
| 1869 | 1707 | ); |
| 1708 | + | |
| 1709 | + /** | |
| 1710 | + * Lesson Attaching with topic ID | |
| 1711 | + * Sorting lesson | |
| 1712 | + */ | |
| 1713 | + if ( isset( $topic['lesson_ids'] ) ) { | |
| 1714 | + $lesson_ids = $topic['lesson_ids']; | |
| 1715 | + } else { | |
| 1716 | + $lesson_ids = array(); | |
| 1717 | + } | |
| 1718 | + if ( count( $lesson_ids ) ) { | |
| 1719 | + foreach ( $lesson_ids as $lesson_key => $lesson_id ) { | |
| 1720 | + $wpdb->update( | |
| 1721 | + $wpdb->posts, | |
| 1722 | + array( | |
| 1723 | + 'post_parent' => $topic['topic_id'], | |
| 1724 | + 'menu_order' => $lesson_key, | |
| 1725 | + ), | |
| 1726 | + array( 'ID' => $lesson_id ) | |
| 1727 | + ); | |
| 1728 | + } | |
| 1729 | + } | |
| 1870 | 1730 | } |
| 1871 | 1731 | } |
| 1872 | 1732 | } |
| 1873 | 1733 | } |
| @@ -1938,14 +1798,12 @@ | ||
| 1938 | 1798 | } |
| 1939 | 1799 | //phpcs:enable WordPress.Security.NonceVerification.Missing |
| 1940 | 1800 | } |
| 1941 | 1801 | |
| 1942 | - $sorting_order = Input::post( 'tutor_topics_lessons_sorting', '' ); | |
| 1943 | - $sorting_order = json_decode( $sorting_order, true ) ?? array(); | |
| 1944 | 1802 | /** |
| 1945 | 1803 | * Sorting Topics and lesson |
| 1946 | 1804 | */ |
| 1947 | - $this->save_course_content_order( $sorting_order ); | |
| 1805 | + $this->save_course_content_order(); | |
| 1948 | 1806 | |
| 1949 | 1807 | // Additional data like course intro video. |
| 1950 | 1808 | if ( $additional_data_edit ) { |
| 1951 | 1809 | // Sanitize data through helper method. |
| @@ -2140,14 +1998,8 @@ | ||
| 2140 | 1998 | */ |
| 2141 | 1999 | |
| 2142 | 2000 | $is_purchasable = tutor_utils()->is_course_purchasable( $course_id ); |
| 2143 | 2001 | |
| 2144 | - $course = get_post( $course_id ); | |
| 2145 | - | |
| 2146 | - if ( 'private' === $course->post_status && ! current_user_can( 'read_private_tutor_courses' ) ) { | |
| 2147 | - wp_send_json_error( __( 'You do not have permission to enroll in this course', 'tutor' ) ); | |
| 2148 | - } | |
| 2149 | - | |
| 2150 | 2002 | /** |
| 2151 | 2003 | * If is is not purchasable, it's free, and enroll right now |
| 2152 | 2004 | * If purchasable, then process purchase. |
| 2153 | 2005 | * |
| @@ -2157,9 +2009,9 @@ | ||
| 2157 | 2009 | // Process purchase. |
| 2158 | 2010 | |
| 2159 | 2011 | } else { |
| 2160 | 2012 | // Free enroll. |
| 2161 | - EnrollmentModel::do_enroll( $course_id ); | |
| 2013 | + tutor_utils()->do_enroll( $course_id ); | |
| 2162 | 2014 | } |
| 2163 | 2015 | |
| 2164 | 2016 | $referer_url = wp_get_referer(); |
| 2165 | 2017 | wp_safe_redirect( tutor_utils()->get_nocache_url( $referer_url ) ); |
| @@ -2169,11 +2021,8 @@ | ||
| 2169 | 2021 | /** |
| 2170 | 2022 | * Mark complete completed |
| 2171 | 2023 | * |
| 2172 | 2024 | * @since 1.0.0 |
| 2173 | - * | |
| 2174 | - * @since 3.7.1 Filter hook: tutor_user_can_complete_course added | |
| 2175 | - * | |
| 2176 | 2025 | * @return void |
| 2177 | 2026 | */ |
| 2178 | 2027 | public function mark_course_complete() { |
| 2179 | 2028 | $tutor_action = Input::post( 'tutor_action' ); |
| @@ -2181,10 +2030,8 @@ | ||
| 2181 | 2030 | if ( 'tutor_complete_course' !== $tutor_action || ! $course_id ) { |
| 2182 | 2031 | return; |
| 2183 | 2032 | } |
| 2184 | 2033 | |
| 2185 | - $permalink = get_the_permalink( $course_id ); | |
| 2186 | - | |
| 2187 | 2034 | // Checking nonce. |
| 2188 | 2035 | tutor_utils()->checking_nonce(); |
| 2189 | 2036 | |
| 2190 | 2037 | $user_id = get_current_user_id(); |
| @@ -2193,78 +2040,20 @@ | ||
| 2193 | 2040 | if ( ! $user_id ) { |
| 2194 | 2041 | die( esc_html__( 'Please Sign-In', 'tutor' ) ); |
| 2195 | 2042 | } |
| 2196 | 2043 | |
| 2197 | - if ( ! EnrollmentModel::is_enrolled( $course_id, $user_id ) ) { | |
| 2198 | - die( esc_html__( 'User is not enrolled in course', 'tutor' ) ); | |
| 2199 | - } | |
| 2044 | + CourseModel::mark_course_as_completed( $course_id, $user_id ); | |
| 2200 | 2045 | |
| 2201 | - /** | |
| 2202 | - * Filter hook provided to restrict course completion. This is useful | |
| 2203 | - * for specific cases like prerequisites. WP_Error should be returned | |
| 2204 | - * from the filter value to prevent the completion. | |
| 2205 | - */ | |
| 2206 | - $can_complete = apply_filters( 'tutor_user_can_complete_course', CourseModel::can_complete_course( $course_id, $user_id ), $user_id, $course_id ); | |
| 2046 | + $permalink = get_the_permalink( $course_id ); | |
| 2207 | 2047 | |
| 2208 | - if ( is_wp_error( $can_complete ) ) { | |
| 2209 | - tutor_utils()->redirect_to( $permalink, $can_complete->get_error_message(), 'error' ); | |
| 2210 | - } elseif ( ! $can_complete ) { | |
| 2211 | - tutor_utils()->redirect_to( $permalink, __( 'You do not have permission to complete this course.', 'tutor' ), 'error' ); | |
| 2212 | - } else { | |
| 2213 | - CourseModel::mark_course_as_completed( $course_id, $user_id ); | |
| 2214 | - // Set temporary identifier to show review pop up. | |
| 2215 | - self::set_review_popup_data( $user_id, $course_id, $permalink ); | |
| 2048 | + // Set temporary identifier to show review pop up. | |
| 2049 | + self::set_review_popup_data( $user_id, $course_id, $permalink ); | |
| 2216 | 2050 | |
| 2217 | - wp_safe_redirect( $permalink ); | |
| 2218 | - exit; | |
| 2219 | - } | |
| 2051 | + wp_safe_redirect( $permalink ); | |
| 2052 | + exit; | |
| 2220 | 2053 | } |
| 2221 | 2054 | |
| 2222 | 2055 | /** |
| 2223 | - * Ajax course complete handler | |
| 2224 | - * | |
| 2225 | - * @since 4.0.0 | |
| 2226 | - * | |
| 2227 | - * @return void | |
| 2228 | - */ | |
| 2229 | - public function ajax_tutor_complete_course() { | |
| 2230 | - $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); | |
| 2231 | - | |
| 2232 | - // Checking nonce. | |
| 2233 | - if ( ! tutor_utils()->is_nonce_verified() ) { | |
| 2234 | - $this->response_bad_request( tutor_utils()->error_message( 'nonce' ) ); | |
| 2235 | - } | |
| 2236 | - | |
| 2237 | - if ( ! $course_id ) { | |
| 2238 | - $this->response_bad_request( __( 'Invalid course', 'tutor' ) ); | |
| 2239 | - } | |
| 2240 | - | |
| 2241 | - $user_id = get_current_user_id(); | |
| 2242 | - if ( ! EnrollmentModel::is_enrolled( $course_id, $user_id ) ) { | |
| 2243 | - $this->response_bad_request( __( 'You are not enrolled in this course', 'tutor' ) ); | |
| 2244 | - } | |
| 2245 | - | |
| 2246 | - /** | |
| 2247 | - * Filter hook provided to restrict course completion. This is useful | |
| 2248 | - * for specific cases like prerequisites. WP_Error should be returned | |
| 2249 | - * from the filter value to prevent the completion. | |
| 2250 | - */ | |
| 2251 | - $can_complete = apply_filters( 'tutor_user_can_complete_course', CourseModel::can_complete_course( $course_id, $user_id ), $user_id, $course_id ); | |
| 2252 | - | |
| 2253 | - if ( is_wp_error( $can_complete ) ) { | |
| 2254 | - $this->response_bad_request( $can_complete->get_error_message() ); | |
| 2255 | - } elseif ( ! $can_complete ) { | |
| 2256 | - $this->response_bad_request( __( 'You do not have permission to complete this course.', 'tutor' ) ); | |
| 2257 | - } else { | |
| 2258 | - CourseModel::mark_course_as_completed( $course_id, $user_id ); | |
| 2259 | - // Set temporary identifier to show review pop up. | |
| 2260 | - self::set_review_popup_data( $user_id, $course_id ); | |
| 2261 | - | |
| 2262 | - $this->response_success( __( 'Course completed successfully', 'tutor' ) ); | |
| 2263 | - } | |
| 2264 | - } | |
| 2265 | - | |
| 2266 | - /** | |
| 2267 | 2056 | * Set data for review popup. |
| 2268 | 2057 | * |
| 2269 | 2058 | * @since 2.2.5 |
| 2270 | 2059 | * @since 2.4.0 removed $permalink param. store user meta instead of option data. |
| @@ -2290,34 +2079,18 @@ | ||
| 2290 | 2079 | * @since 1.0.0 |
| 2291 | 2080 | * @return void |
| 2292 | 2081 | */ |
| 2293 | 2082 | public function popup_review_form() { |
| 2294 | - if ( ! is_user_logged_in() ) { | |
| 2295 | - return; | |
| 2296 | - } | |
| 2083 | + if ( is_user_logged_in() ) { | |
| 2084 | + $user_id = get_current_user_id(); | |
| 2085 | + $course_id = get_the_ID(); | |
| 2086 | + $meta_key = User::get_review_popup_meta( $course_id ); | |
| 2087 | + $review_course_id = (int) get_user_meta( $user_id, $meta_key, true ); | |
| 2297 | 2088 | |
| 2298 | - $is_learning_area = tutor_utils()->is_learning_area(); | |
| 2299 | - $is_legacy_learning = tutor_utils()->is_legacy_learning_mode(); | |
| 2300 | - if ( $is_legacy_learning && $is_learning_area ) { | |
| 2301 | - return; | |
| 2089 | + if ( is_single() && $course_id === $review_course_id ) { | |
| 2090 | + include tutor()->path . 'views/modal/review.php'; | |
| 2091 | + } | |
| 2302 | 2092 | } |
| 2303 | - | |
| 2304 | - $course_id = get_the_ID(); | |
| 2305 | - | |
| 2306 | - if ( $is_learning_area && ! Input::has( 'subpage' ) ) { | |
| 2307 | - $course_id = wp_get_post_parent_id( wp_get_post_parent_id( get_the_ID() ) ); | |
| 2308 | - } | |
| 2309 | - | |
| 2310 | - if ( empty( $course_id ) ) { | |
| 2311 | - return; | |
| 2312 | - } | |
| 2313 | - | |
| 2314 | - $user_id = get_current_user_id(); | |
| 2315 | - $meta_key = User::get_review_popup_meta( $course_id ); | |
| 2316 | - $review_course_id = (int) get_user_meta( $user_id, $meta_key, true ); | |
| 2317 | - if ( is_single() && $course_id === $review_course_id ) { | |
| 2318 | - include tutor()->path . 'views/modal/review.php'; | |
| 2319 | - } | |
| 2320 | 2093 | } |
| 2321 | 2094 | |
| 2322 | 2095 | /** |
| 2323 | 2096 | * Review popup data clear |
| @@ -2345,9 +2118,8 @@ | ||
| 2345 | 2118 | /** |
| 2346 | 2119 | * Delete course delete from frontend dashboard |
| 2347 | 2120 | * |
| 2348 | 2121 | * @since 2.0.0 |
| 2349 | - * | |
| 2350 | 2122 | * @return void |
| 2351 | 2123 | */ |
| 2352 | 2124 | public function tutor_delete_dashboard_course() { |
| 2353 | 2125 | tutor_utils()->checking_nonce(); |
| @@ -2366,9 +2138,9 @@ | ||
| 2366 | 2138 | wp_send_json_error( array( 'message' => __( 'Only main instructor can delete this course', 'tutor' ) ) ); |
| 2367 | 2139 | } |
| 2368 | 2140 | |
| 2369 | 2141 | // Check if user is only an instructor. |
| 2370 | - if ( ! User::is_admin() ) { | |
| 2142 | + if ( ! current_user_can( 'administrator' ) ) { | |
| 2371 | 2143 | // Check if instructor can trash course. |
| 2372 | 2144 | $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ); |
| 2373 | 2145 | |
| 2374 | 2146 | if ( ! $can_trash_post ) { |
| @@ -2378,9 +2150,9 @@ | ||
| 2378 | 2150 | |
| 2379 | 2151 | $trash_course = wp_update_post( |
| 2380 | 2152 | array( |
| 2381 | 2153 | 'ID' => $course_id, |
| 2382 | - 'post_status' => CourseModel::STATUS_TRASH, | |
| 2154 | + 'post_status' => 'trash', | |
| 2383 | 2155 | ) |
| 2384 | 2156 | ); |
| 2385 | 2157 | |
| 2386 | 2158 | if ( $trash_course ) { |
| @@ -2439,24 +2211,21 @@ | ||
| 2439 | 2211 | * |
| 2440 | 2212 | * @return void |
| 2441 | 2213 | */ |
| 2442 | 2214 | public function attach_product_with_course( $post_ID, $post_data ) { |
| 2215 | + | |
| 2443 | 2216 | $monetize_by = tutor_utils()->get_option( 'monetize_by' ); |
| 2444 | 2217 | $product_id = Input::post( '_tutor_course_product_id', 0, Input::TYPE_INT ); |
| 2445 | 2218 | |
| 2446 | - /** | |
| 2447 | - * For native monetization, just return | |
| 2448 | - * No need to attach anything. | |
| 2449 | - */ | |
| 2450 | 2219 | if ( Ecommerce::MONETIZE_BY === $monetize_by ) { |
| 2451 | 2220 | return; |
| 2452 | 2221 | } |
| 2453 | 2222 | |
| 2454 | 2223 | /** |
| 2455 | - * When course moved paid to free | |
| 2456 | - * Keep the product linked and return. | |
| 2224 | + * Unlink product from course. | |
| 2457 | 2225 | */ |
| 2458 | 2226 | if ( -1 === $product_id ) { |
| 2227 | + delete_post_meta( $post_ID, self::COURSE_PRODUCT_ID_META ); | |
| 2459 | 2228 | return; |
| 2460 | 2229 | } |
| 2461 | 2230 | |
| 2462 | 2231 | /** |
| @@ -2483,31 +2252,36 @@ | ||
| 2483 | 2252 | update_post_meta( $post_ID, self::COURSE_PRICE_TYPE_META, self::PRICE_TYPE_PAID ); |
| 2484 | 2253 | |
| 2485 | 2254 | if ( 'wc' === $monetize_by ) { |
| 2486 | 2255 | |
| 2487 | - $is_update = ( $product_id && wc_get_product( $product_id ) ) ? true : false; | |
| 2256 | + $is_update = ( $attached_product_id && wc_get_product( $attached_product_id ) ) ? true : false; | |
| 2488 | 2257 | |
| 2489 | 2258 | if ( $is_update ) { |
| 2259 | + $attached_product_id = $product_id; | |
| 2490 | 2260 | update_post_meta( $post_ID, self::COURSE_PRODUCT_ID_META, $product_id ); |
| 2491 | 2261 | |
| 2492 | - $product_id = self::create_wc_product( $course->post_title, $course_price, $sale_price, $product_id ); | |
| 2262 | + $product_id = self::create_wc_product( $course->post_title, $course_price, $sale_price, $attached_product_id ); | |
| 2493 | 2263 | $product_obj = wc_get_product( $product_id ); |
| 2494 | 2264 | if ( $product_obj->is_type( 'subscription' ) ) { |
| 2495 | - update_post_meta( $product_id, '_subscription_price', $course_price ); | |
| 2265 | + update_post_meta( $attached_product_id, '_subscription_price', $course_price ); | |
| 2496 | 2266 | } |
| 2497 | 2267 | |
| 2498 | 2268 | // Set course regular & sale price. |
| 2499 | - self::set_course_regular_and_sale_price( $post_ID, $product_obj->get_regular_price(), $product_obj->get_sale_price() ); | |
| 2269 | + update_post_meta( $post_ID, self::COURSE_PRICE_META, $product_obj->get_regular_price() ); | |
| 2270 | + update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $product_obj->get_sale_price() ); | |
| 2500 | 2271 | } else { |
| 2501 | 2272 | // Create new WC product name with course title. |
| 2502 | 2273 | $product_id = self::create_wc_product( $course->post_title, $course_price, $sale_price ); |
| 2503 | 2274 | if ( $product_id ) { |
| 2504 | 2275 | $product_obj = wc_get_product( $product_id ); |
| 2276 | + update_post_meta( $post_ID, self::COURSE_PRODUCT_ID_META, $product_id ); | |
| 2277 | + // Mark product for woocommerce. | |
| 2278 | + update_post_meta( $product_id, '_virtual', 'yes' ); | |
| 2279 | + update_post_meta( $product_id, '_tutor_product', 'yes' ); | |
| 2505 | 2280 | |
| 2506 | - self::sync_course_with_wc_product( $post_ID, $product_id ); | |
| 2507 | - | |
| 2508 | 2281 | // Set course regular & sale price. |
| 2509 | - self::set_course_regular_and_sale_price( $post_ID, $product_obj->get_regular_price(), $product_obj->get_sale_price() ); | |
| 2282 | + update_post_meta( $post_ID, self::COURSE_PRICE_META, $product_obj->get_regular_price() ); | |
| 2283 | + update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $product_obj->get_sale_price() ); | |
| 2510 | 2284 | } |
| 2511 | 2285 | } |
| 2512 | 2286 | |
| 2513 | 2287 | $course_post_thumbnail = Input::post( 'thumbnail_id', 0, Input::TYPE_INT ); |
| @@ -2555,8 +2329,33 @@ | ||
| 2555 | 2329 | } |
| 2556 | 2330 | } |
| 2557 | 2331 | |
| 2558 | 2332 | /** |
| 2333 | + * Add Course level to course settings | |
| 2334 | + * | |
| 2335 | + * @since 1.4.1 | |
| 2336 | + * | |
| 2337 | + * @param array $args arguments. | |
| 2338 | + * @return array | |
| 2339 | + */ | |
| 2340 | + public function add_course_level_to_settings( $args ) { | |
| 2341 | + $course_id = get_the_ID(); | |
| 2342 | + $levels = tutor_utils()->course_levels(); | |
| 2343 | + $course_level = get_post_meta( $course_id, '_tutor_course_level', true ); | |
| 2344 | + | |
| 2345 | + $args['general']['fields']['_tutor_course_level'] = array( | |
| 2346 | + 'type' => 'select', | |
| 2347 | + 'label' => __( 'Difficulty Level', 'tutor' ), | |
| 2348 | + 'label_title' => __( 'Enable', 'tutor' ), | |
| 2349 | + 'options' => $levels, | |
| 2350 | + 'value' => $course_level ? $course_level : 'intermediate', | |
| 2351 | + 'desc' => __( 'Course difficulty level', 'tutor' ), | |
| 2352 | + ); | |
| 2353 | + | |
| 2354 | + return $args; | |
| 2355 | + } | |
| 2356 | + | |
| 2357 | + /** | |
| 2559 | 2358 | * Check if course starting |
| 2560 | 2359 | * |
| 2561 | 2360 | * @since 1.4.8 |
| 2562 | 2361 | * @return void |
| @@ -2576,9 +2375,8 @@ | ||
| 2576 | 2375 | /** |
| 2577 | 2376 | * Add Course level to course settings |
| 2578 | 2377 | * |
| 2579 | 2378 | * @since 1.4.8 |
| 2580 | - * | |
| 2581 | 2379 | * @return void |
| 2582 | 2380 | */ |
| 2583 | 2381 | public function course_elements_enable_disable() { |
| 2584 | 2382 | add_filter( 'tutor_course/single/completing-progress-bar', array( $this, 'enable_disable_course_progress_bar' ) ); |
| @@ -2611,9 +2409,8 @@ | ||
| 2611 | 2409 | * |
| 2612 | 2410 | * @since 1.4.8 |
| 2613 | 2411 | * |
| 2614 | 2412 | * @param string $html HTML string. |
| 2615 | - * | |
| 2616 | 2413 | * @return string |
| 2617 | 2414 | */ |
| 2618 | 2415 | public function enable_disable_material_includes( $html ) { |
| 2619 | 2416 | $disable_option = ! (bool) get_tutor_option( 'enable_course_material', true, true ); |
| @@ -2628,9 +2425,8 @@ | ||
| 2628 | 2425 | * |
| 2629 | 2426 | * @since 1.4.8 |
| 2630 | 2427 | * |
| 2631 | 2428 | * @param string $html HTML string. |
| 2632 | - * | |
| 2633 | 2429 | * @return string |
| 2634 | 2430 | */ |
| 2635 | 2431 | public function enable_disable_course_content( $html ) { |
| 2636 | 2432 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_description', true, true ); |
| @@ -2645,9 +2441,8 @@ | ||
| 2645 | 2441 | * |
| 2646 | 2442 | * @since 1.4.8 |
| 2647 | 2443 | * |
| 2648 | 2444 | * @param string $html HTML string. |
| 2649 | - * | |
| 2650 | 2445 | * @return string |
| 2651 | 2446 | */ |
| 2652 | 2447 | public function enable_disable_course_benefits( $html ) { |
| 2653 | 2448 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_benefits', true, true ); |
| @@ -2662,9 +2457,8 @@ | ||
| 2662 | 2457 | * |
| 2663 | 2458 | * @since 1.4.8 |
| 2664 | 2459 | * |
| 2665 | 2460 | * @param string $html HTML string. |
| 2666 | - * | |
| 2667 | 2461 | * @return string |
| 2668 | 2462 | */ |
| 2669 | 2463 | public function enable_disable_course_requirements( $html ) { |
| 2670 | 2464 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_requirements', true, true ); |
| @@ -2679,9 +2473,8 @@ | ||
| 2679 | 2473 | * |
| 2680 | 2474 | * @since 1.4.8 |
| 2681 | 2475 | * |
| 2682 | 2476 | * @param string $html HTML string. |
| 2683 | - * | |
| 2684 | 2477 | * @return string |
| 2685 | 2478 | */ |
| 2686 | 2479 | public function enable_disable_course_target_audience( $html ) { |
| 2687 | 2480 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_target_audience', true, true ); |
| @@ -2726,9 +2519,9 @@ | ||
| 2726 | 2519 | unset( $items['reviews'] ); |
| 2727 | 2520 | } |
| 2728 | 2521 | |
| 2729 | 2522 | // Whether enrollment require. |
| 2730 | - $is_enrolled = EnrollmentModel::is_enrolled(); | |
| 2523 | + $is_enrolled = tutor_utils()->is_enrolled(); | |
| 2731 | 2524 | |
| 2732 | 2525 | return array_filter( |
| 2733 | 2526 | $items, |
| 2734 | 2527 | function ( $item ) use ( $is_enrolled ) { |
| @@ -2743,9 +2536,8 @@ | ||
| 2743 | 2536 | /** |
| 2744 | 2537 | * Filter product in shop page |
| 2745 | 2538 | * |
| 2746 | 2539 | * @since 1.4.9 |
| 2747 | - * | |
| 2748 | 2540 | * @return void|null |
| 2749 | 2541 | */ |
| 2750 | 2542 | public function filter_product_in_shop_page() { |
| 2751 | 2543 | $hide_course_from_shop_page = (bool) get_tutor_option( 'hide_course_from_shop_page' ); |
| @@ -2761,9 +2553,8 @@ | ||
| 2761 | 2553 | /** |
| 2762 | 2554 | * Tutor product meta query |
| 2763 | 2555 | * |
| 2764 | 2556 | * @since 1.4.9 |
| 2765 | - * | |
| 2766 | 2557 | * @return array |
| 2767 | 2558 | */ |
| 2768 | 2559 | public function tutor_product_meta_query() { |
| 2769 | 2560 | $meta_query = array( |
| @@ -2778,9 +2569,8 @@ | ||
| 2778 | 2569 | * |
| 2779 | 2570 | * @since 1.4.9 |
| 2780 | 2571 | * |
| 2781 | 2572 | * @param \WP_Query $wp_query WP Query instance. |
| 2782 | - * | |
| 2783 | 2573 | * @return \WP_Query |
| 2784 | 2574 | */ |
| 2785 | 2575 | public function filter_woocommerce_product_query( $wp_query ) { |
| 2786 | 2576 | $product_ids = $this->get_connected_wc_product_ids(); |
| @@ -2822,9 +2612,8 @@ | ||
| 2822 | 2612 | * |
| 2823 | 2613 | * @since 1.4.9 |
| 2824 | 2614 | * |
| 2825 | 2615 | * @param \WP_Query $query WP Query instance. |
| 2826 | - * | |
| 2827 | 2616 | * @return \WP_Query |
| 2828 | 2617 | */ |
| 2829 | 2618 | public function filter_edd_downloads_query( $query ) { |
| 2830 | 2619 | $query['meta_query'][] = $this->tutor_product_meta_query(); |
| @@ -2836,9 +2625,8 @@ | ||
| 2836 | 2625 | * |
| 2837 | 2626 | * @since 1.4.9 |
| 2838 | 2627 | * |
| 2839 | 2628 | * @param \WP_Query $wp_query WP Query instance. |
| 2840 | - * | |
| 2841 | 2629 | * @return \WP_Query |
| 2842 | 2630 | */ |
| 2843 | 2631 | public function filter_archive_meta_query( $wp_query ) { |
| 2844 | 2632 | if ( ! is_admin() && $wp_query->is_archive && $wp_query->get( 'post_type' ) === 'download' ) { |
| @@ -2852,9 +2640,8 @@ | ||
| 2852 | 2640 | * |
| 2853 | 2641 | * @since 1.5.8 |
| 2854 | 2642 | * |
| 2855 | 2643 | * @param string $html HTML string. |
| 2856 | - * | |
| 2857 | 2644 | * @return string |
| 2858 | 2645 | */ |
| 2859 | 2646 | public function remove_price_if_enrolled( $html ) { |
| 2860 | 2647 | $should_removed = apply_filters( 'should_remove_price_if_enrolled', true ); |
| @@ -2860,9 +2647,9 @@ | ||
| 2860 | 2647 | $should_removed = apply_filters( 'should_remove_price_if_enrolled', true ); |
| 2861 | 2648 | |
| 2862 | 2649 | if ( $should_removed ) { |
| 2863 | 2650 | $course_id = get_the_ID(); |
| 2864 | - $enrolled = EnrollmentModel::is_enrolled( $course_id ); | |
| 2651 | + $enrolled = tutor_utils()->is_enrolled( $course_id ); | |
| 2865 | 2652 | if ( $enrolled ) { |
| 2866 | 2653 | $html = ''; |
| 2867 | 2654 | } |
| 2868 | 2655 | } |
| @@ -2869,119 +2656,112 @@ | ||
| 2869 | 2656 | return $html; |
| 2870 | 2657 | } |
| 2871 | 2658 | |
| 2872 | 2659 | /** |
| 2873 | - * Get course completion missing requirements message. | |
| 2660 | + * Check if all lessons and quizzes done before mark course complete. | |
| 2874 | 2661 | * |
| 2875 | - * @since 4.0.0 | |
| 2662 | + * @since 1.5.8 | |
| 2876 | 2663 | * |
| 2877 | - * @param int $course_id Course ID. | |
| 2878 | - * @param int $user_id User ID. | |
| 2879 | - * | |
| 2880 | - * @return string|null | |
| 2664 | + * @param string $html HTML string. | |
| 2665 | + * @return string | |
| 2881 | 2666 | */ |
| 2882 | - public static function get_course_completion_restrict_msg( $course_id = 0, $user_id = 0 ) { | |
| 2883 | - $course_id = tutor_utils()->get_post_id( $course_id ); | |
| 2884 | - $user_id = tutor_utils()->get_user_id( $user_id ); | |
| 2667 | + public function tutor_lms_hide_course_complete_btn( $html ) { | |
| 2885 | 2668 | |
| 2886 | - if ( 'strict' !== tutor_utils()->get_option( 'course_completion_process' ) ) { | |
| 2887 | - return null; | |
| 2669 | + $completion_mode = tutor_utils()->get_option( 'course_completion_process' ); | |
| 2670 | + if ( 'strict' !== $completion_mode ) { | |
| 2671 | + return $html; | |
| 2888 | 2672 | } |
| 2889 | 2673 | |
| 2890 | - $completed_lessons = tutor_utils()->get_completed_lesson_count_by_course( $course_id, $user_id ); | |
| 2891 | - $total_lessons = tutor_utils()->get_lesson_count_by_course( $course_id ); | |
| 2674 | + $completed_lesson = tutor_utils()->get_completed_lesson_count_by_course(); | |
| 2675 | + $lesson_count = tutor_utils()->get_lesson_count_by_course(); | |
| 2892 | 2676 | |
| 2893 | - if ( $completed_lessons < $total_lessons ) { | |
| 2894 | - return __( 'Complete all lessons to mark this course as complete', 'tutor' ); | |
| 2677 | + if ( $completed_lesson < $lesson_count ) { | |
| 2678 | + return '<div class="tutor-alert tutor-warning tutor-mt-28"> | |
| 2679 | + <div class="tutor-alert-text"> | |
| 2680 | + <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span> | |
| 2681 | + <span>' . __( 'Complete all lessons to mark this course as complete', 'tutor' ) . '</span> | |
| 2682 | + </div> | |
| 2683 | + </div>'; | |
| 2895 | 2684 | } |
| 2896 | 2685 | |
| 2897 | - $course_contents = tutor_utils()->get_course_contents_by_id( $course_id ); | |
| 2686 | + $quizzes = array(); | |
| 2687 | + $assignments = array(); | |
| 2898 | 2688 | |
| 2899 | - $required_quiz_pass_count = 0; | |
| 2900 | - $required_assignment_pass_count = 0; | |
| 2689 | + $course_contents = tutor_utils()->get_course_contents_by_id(); | |
| 2690 | + if ( tutor_utils()->count( $course_contents ) ) { | |
| 2691 | + foreach ( $course_contents as $content ) { | |
| 2692 | + if ( 'tutor_quiz' === $content->post_type ) { | |
| 2693 | + $quizzes[] = $content; | |
| 2694 | + } | |
| 2695 | + if ( 'tutor_assignments' === $content->post_type ) { | |
| 2696 | + $assignments[] = $content; | |
| 2697 | + } | |
| 2698 | + } | |
| 2699 | + } | |
| 2901 | 2700 | |
| 2902 | - $is_assignment_addon_enabled = tutor_utils()->is_addon_enabled( 'tutor-assignments' ); | |
| 2701 | + $required_assignment_pass = 0; | |
| 2903 | 2702 | |
| 2904 | - foreach ( $course_contents as $content ) { | |
| 2703 | + foreach ( $assignments as $row ) { | |
| 2905 | 2704 | |
| 2906 | - if ( tutor()->quiz_post_type === $content->post_type ) { | |
| 2907 | - if ( ! QuizModel::is_quiz_passed( $content->ID, $user_id ) ) { | |
| 2908 | - ++$required_quiz_pass_count; | |
| 2909 | - } | |
| 2910 | - } | |
| 2705 | + $submitted_assignment = tutor_utils()->is_assignment_submitted( $row->ID ); | |
| 2706 | + $is_reviewed_by_instructor = null === $submitted_assignment | |
| 2707 | + ? false | |
| 2708 | + : get_comment_meta( $submitted_assignment->comment_ID, 'evaluate_time', true ); | |
| 2911 | 2709 | |
| 2912 | - if ( $is_assignment_addon_enabled && tutor()->assignment_post_type === $content->post_type ) { | |
| 2913 | - if ( ! Assignments::is_assignment_passed( $content->ID, $user_id ) ) { | |
| 2914 | - ++$required_assignment_pass_count; | |
| 2710 | + if ( $submitted_assignment && $is_reviewed_by_instructor ) { | |
| 2711 | + $pass_mark = tutor_utils()->get_assignment_option( $submitted_assignment->comment_post_ID, 'pass_mark' ); | |
| 2712 | + $given_mark = get_comment_meta( $submitted_assignment->comment_ID, 'assignment_mark', true ); | |
| 2713 | + | |
| 2714 | + if ( $given_mark < $pass_mark ) { | |
| 2715 | + $required_assignment_pass++; | |
| 2915 | 2716 | } |
| 2717 | + } else { | |
| 2718 | + $required_assignment_pass++; | |
| 2916 | 2719 | } |
| 2917 | 2720 | } |
| 2918 | 2721 | |
| 2919 | - if ( ! $required_quiz_pass_count && ! $required_assignment_pass_count ) { | |
| 2920 | - return null; | |
| 2921 | - } | |
| 2722 | + $is_quiz_pass = true; | |
| 2723 | + $required_quiz_pass = 0; | |
| 2922 | 2724 | |
| 2923 | - return self::get_course_completion_requirement_message( | |
| 2924 | - $required_quiz_pass_count, | |
| 2925 | - $required_assignment_pass_count | |
| 2926 | - ); | |
| 2927 | - } | |
| 2725 | + if ( tutor_utils()->count( $quizzes ) ) { | |
| 2726 | + foreach ( $quizzes as $quiz ) { | |
| 2928 | 2727 | |
| 2929 | - /** | |
| 2930 | - * Build missing course completion requirements message. | |
| 2931 | - * | |
| 2932 | - * @since 4.0.0 | |
| 2933 | - * | |
| 2934 | - * @param int $quiz_count Total quiz count. | |
| 2935 | - * @param int $assignment_count Total assignment count. | |
| 2936 | - * | |
| 2937 | - * @return string | |
| 2938 | - */ | |
| 2939 | - private static function get_course_completion_requirement_message( $quiz_count, $assignment_count ) { | |
| 2940 | - $quiz_label = _n( 'quiz', 'quizzes', $quiz_count, 'tutor' ); | |
| 2941 | - $assignment_label = _n( 'assignment', 'assignments', $assignment_count, 'tutor' ); | |
| 2728 | + $attempt = tutor_utils()->get_quiz_attempt( $quiz->ID ); | |
| 2729 | + if ( $attempt ) { | |
| 2730 | + $passing_grade = tutor_utils()->get_quiz_option( $quiz->ID, 'passing_grade', 0 ); | |
| 2731 | + $earned_percentage = $attempt->earned_marks > 0 ? ( number_format( ( $attempt->earned_marks * 100 ) / $attempt->total_marks ) ) : 0; | |
| 2942 | 2732 | |
| 2943 | - if ( $quiz_count && ! $assignment_count ) { | |
| 2944 | - return sprintf( | |
| 2945 | - /* translators: %1$s: item count; %2$s: item label */ | |
| 2946 | - __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), | |
| 2947 | - $quiz_count, | |
| 2948 | - $quiz_label | |
| 2949 | - ); | |
| 2733 | + if ( $earned_percentage < $passing_grade ) { | |
| 2734 | + $required_quiz_pass++; | |
| 2735 | + $is_quiz_pass = false; | |
| 2736 | + } | |
| 2737 | + } else { | |
| 2738 | + $required_quiz_pass++; | |
| 2739 | + $is_quiz_pass = false; | |
| 2740 | + } | |
| 2741 | + } | |
| 2950 | 2742 | } |
| 2951 | 2743 | |
| 2952 | - if ( ! $quiz_count && $assignment_count ) { | |
| 2953 | - return sprintf( | |
| 2954 | - /* translators: %1$s: item count; %2$s: item label */ | |
| 2955 | - __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), | |
| 2956 | - $assignment_count, | |
| 2957 | - $assignment_label | |
| 2958 | - ); | |
| 2959 | - } | |
| 2744 | + if ( ! $is_quiz_pass || $required_assignment_pass > 0 ) { | |
| 2745 | + $_msg = ''; | |
| 2746 | + $quiz_str = _n( 'quiz', 'quizzes', $required_quiz_pass, 'tutor' ); | |
| 2747 | + $assignment_str = _n( 'assignment', 'assignments', $required_assignment_pass, 'tutor' ); | |
| 2960 | 2748 | |
| 2961 | - return sprintf( | |
| 2962 | - /* translators: %1$s: quiz count; %2$s: quiz label; %3$s: assignment count; %4$s: assignment label */ | |
| 2963 | - __( 'You have to pass %1$s %2$s and %3$s %4$s to complete this course.', 'tutor' ), | |
| 2964 | - $quiz_count, | |
| 2965 | - $quiz_label, | |
| 2966 | - $assignment_count, | |
| 2967 | - $assignment_label | |
| 2968 | - ); | |
| 2969 | - } | |
| 2749 | + if ( ! $is_quiz_pass && 0 == $required_assignment_pass ) { | |
| 2750 | + /* translators: %1$s: number of quiz/assignment pass required; %2$s: quiz/assignment string */ | |
| 2751 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str ); | |
| 2752 | + } | |
| 2970 | 2753 | |
| 2971 | - /** | |
| 2972 | - * Check if all lessons and quizzes done before mark course complete. | |
| 2973 | - * | |
| 2974 | - * @since 1.5.8 | |
| 2975 | - * | |
| 2976 | - * @param string $html HTML string. | |
| 2977 | - * | |
| 2978 | - * @return string | |
| 2979 | - */ | |
| 2980 | - public function tutor_lms_hide_course_complete_btn( $html ) { | |
| 2981 | - $_msg = self::get_course_completion_restrict_msg(); | |
| 2754 | + if ( $is_quiz_pass && $required_assignment_pass > 0 ) { | |
| 2755 | + //phpcs:ignore | |
| 2756 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_assignment_pass, $assignment_str ); | |
| 2757 | + } | |
| 2982 | 2758 | |
| 2983 | - if ( $_msg ) { | |
| 2759 | + if ( ! $is_quiz_pass && $required_assignment_pass > 0 ) { | |
| 2760 | + /* translators: %1$s: number of quiz pass required; %2$s: quiz string; %3$s: number of assignment pass required; %4$s: assignment string */ | |
| 2761 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s and %3$s %4$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str, $required_assignment_pass, $assignment_str ); | |
| 2762 | + } | |
| 2763 | + | |
| 2984 | 2764 | return '<div class="tutor-alert tutor-warning tutor-mt-28"> |
| 2985 | 2765 | <div class="tutor-alert-text"> |
| 2986 | 2766 | <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span> |
| 2987 | 2767 | <span>' . $_msg . '</span> |
| @@ -2997,9 +2777,8 @@ | ||
| 2997 | 2777 | * |
| 2998 | 2778 | * @since 1.5.8 |
| 2999 | 2779 | * |
| 3000 | 2780 | * @param string $html HTML string. |
| 3001 | - * | |
| 3002 | 2781 | * @return string |
| 3003 | 2782 | */ |
| 3004 | 2783 | public function get_generate_greadbook( $html ) { |
| 3005 | 2784 | if ( ! tutor_utils()->is_completed_course() ) { |
| @@ -3011,9 +2790,8 @@ | ||
| 3011 | 2790 | /** |
| 3012 | 2791 | * Add social share content in header |
| 3013 | 2792 | * |
| 3014 | 2793 | * @since 1.6.3 |
| 3015 | - * | |
| 3016 | 2794 | * @return void |
| 3017 | 2795 | */ |
| 3018 | 2796 | public function social_share_content() { |
| 3019 | 2797 | global $wp_query, $post; |
| @@ -3037,9 +2815,8 @@ | ||
| 3037 | 2815 | * |
| 3038 | 2816 | * @since 1.8.2 |
| 3039 | 2817 | * |
| 3040 | 2818 | * @param integer $post_id post ID. |
| 3041 | - * | |
| 3042 | 2819 | * @return void |
| 3043 | 2820 | */ |
| 3044 | 2821 | public function delete_associated_enrollment( $post_id ) { |
| 3045 | 2822 | global $wpdb; |
| @@ -3050,12 +2827,11 @@ | ||
| 3050 | 2827 | post_id |
| 3051 | 2828 | FROM |
| 3052 | 2829 | {$wpdb->postmeta} |
| 3053 | 2830 | WHERE |
| 3054 | - meta_key=%s | |
| 2831 | + meta_key='_tutor_enrolled_by_order_id' | |
| 3055 | 2832 | AND meta_value = %d |
| 3056 | 2833 | ", |
| 3057 | - EnrollmentModel::ENROLLMENT_ORDER_ID_META, | |
| 3058 | 2834 | $post_id |
| 3059 | 2835 | ) |
| 3060 | 2836 | ); |
| 3061 | 2837 | |
| @@ -3071,35 +2847,21 @@ | ||
| 3071 | 2847 | /** |
| 3072 | 2848 | * Reset course progress. |
| 3073 | 2849 | * |
| 3074 | 2850 | * @since 1.5.8 |
| 3075 | - * | |
| 3076 | 2851 | * @return void |
| 3077 | 2852 | */ |
| 3078 | 2853 | public function tutor_reset_course_progress() { |
| 3079 | 2854 | tutor_utils()->checking_nonce(); |
| 3080 | - $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); | |
| 3081 | - $context = Input::post( 'context', '' ); | |
| 3082 | - $course_reset_progress = tutor_utils()->get_option( 'course_reset_progress', false ); | |
| 3083 | - $course_retake_feature = tutor_utils()->get_option( 'course_retake_feature', false ); | |
| 2855 | + $course_id = Input::post( 'course_id' ); | |
| 3084 | 2856 | |
| 3085 | - if ( ! $course_reset_progress && 'learning-area-sidebar' === $context ) { | |
| 3086 | - $this->response_bad_request( __( 'You do not have permission to reset this course.', 'tutor' ) ); | |
| 2857 | + if ( ! $course_id || ! is_numeric( $course_id ) || ! tutor_utils()->is_enrolled( $course_id ) ) { | |
| 2858 | + wp_send_json_error( array( 'message' => __( 'Invalid Course ID or Access Denied.', 'tutor' ) ) ); | |
| 3087 | 2859 | return; |
| 3088 | 2860 | } |
| 3089 | 2861 | |
| 3090 | - if ( ! $course_retake_feature && ( 'course-landing' === $context || 'learning-area' === $context ) ) { | |
| 3091 | - $this->response_bad_request( __( 'You do not have permission to retake this course.', 'tutor' ) ); | |
| 3092 | - return; | |
| 3093 | - } | |
| 3094 | - | |
| 3095 | - if ( ! $course_id || ! is_numeric( $course_id ) || ! EnrollmentModel::is_enrolled( $course_id ) ) { | |
| 3096 | - $this->response_bad_request( __( 'Invalid Course ID or Access Denied.', 'tutor' ) ); | |
| 3097 | - return; | |
| 3098 | - } | |
| 3099 | - | |
| 3100 | 2862 | tutor_utils()->delete_course_progress( $course_id ); |
| 3101 | - $this->json_response( '', array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) ); | |
| 2863 | + wp_send_json_success( array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) ); | |
| 3102 | 2864 | } |
| 3103 | 2865 | |
| 3104 | 2866 | /** |
| 3105 | 2867 | * Do enroll if guest attempt to enroll and course is free |
| @@ -3107,9 +2869,9 @@ | ||
| 3107 | 2869 | * @since 1.9.8 |
| 3108 | 2870 | * |
| 3109 | 2871 | * @param integer $course_id course ID. |
| 3110 | 2872 | * @param integer $user_id user ID. |
| 3111 | - * | |
| 2873 | + | |
| 3112 | 2874 | * @return void |
| 3113 | 2875 | */ |
| 3114 | 2876 | public function enroll_after_login_if_attempt( int $course_id, int $user_id ) { |
| 3115 | 2877 | $course_id = sanitize_text_field( $course_id ); |
| @@ -3117,9 +2879,9 @@ | ||
| 3117 | 2879 | |
| 3118 | 2880 | if ( $course_id && $is_allowed ) { |
| 3119 | 2881 | $is_purchasable = tutor_utils()->is_course_purchasable( $course_id ); |
| 3120 | 2882 | if ( ! $is_purchasable ) { |
| 3121 | - EnrollmentModel::do_enroll( $course_id, $order_id = 0, $user_id ); | |
| 2883 | + tutor_utils()->do_enroll( $course_id, $order_id = 0, $user_id ); | |
| 3122 | 2884 | do_action( 'guest_attempt_after_enrollment', $course_id ); |
| 3123 | 2885 | } |
| 3124 | 2886 | } |
| 3125 | 2887 | } |
| @@ -3127,9 +2889,8 @@ | ||
| 3127 | 2889 | /** |
| 3128 | 2890 | * Handle course enrollment |
| 3129 | 2891 | * |
| 3130 | 2892 | * @since 2.1.0 |
| 3131 | - * | |
| 3132 | 2893 | * @return void |
| 3133 | 2894 | */ |
| 3134 | 2895 | public function course_enrollment() { |
| 3135 | 2896 | tutor_utils()->checking_nonce(); |
| @@ -3136,46 +2897,21 @@ | ||
| 3136 | 2897 | |
| 3137 | 2898 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 3138 | 2899 | $user_id = get_current_user_id(); |
| 3139 | 2900 | |
| 3140 | - if ( ! $course_id || ! $user_id ) { | |
| 3141 | - wp_send_json_error( tutor_utils()->error_message( 'invalid_req' ) ); | |
| 3142 | - } | |
| 3143 | - | |
| 3144 | - $password_protected = post_password_required( $course_id ); | |
| 3145 | - if ( $password_protected ) { | |
| 3146 | - wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); | |
| 3147 | - } | |
| 3148 | - | |
| 3149 | - $course = get_post( $course_id ); | |
| 3150 | - | |
| 3151 | - if ( CourseModel::STATUS_PRIVATE === $course->post_status && ! current_user_can( 'read_private_tutor_courses' ) ) { | |
| 3152 | - wp_send_json_error( __( 'You do not have permission to enroll in this course', 'tutor' ) ); | |
| 3153 | - } | |
| 3154 | - | |
| 3155 | - /** | |
| 3156 | - * This check was added to address a security issue where users could | |
| 3157 | - * enroll in a course via an AJAX call without purchasing it. | |
| 3158 | - * | |
| 3159 | - * To prevent this, we now verify whether the course is paid. | |
| 3160 | - * Additionally, we check if the user is already enrolled, since | |
| 3161 | - * Tutor's default behavior enrolls users automatically upon purchase. | |
| 3162 | - * | |
| 3163 | - * @since 3.9.4 | |
| 3164 | - */ | |
| 3165 | - if ( tutor_utils()->is_course_purchasable( $course_id ) ) { | |
| 3166 | - $is_enrolled = (bool) EnrollmentModel::is_enrolled( $course_id, $user_id ); | |
| 3167 | - | |
| 3168 | - if ( ! $is_enrolled ) { | |
| 3169 | - wp_send_json_error( __( 'Please purchase the course before enrolling', 'tutor' ) ); | |
| 2901 | + if ( $course_id ) { | |
| 2902 | + $password_protected = post_password_required( $course_id ); | |
| 2903 | + if ( $password_protected ) { | |
| 2904 | + wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); | |
| 3170 | 2905 | } |
| 3171 | - } | |
| 3172 | - | |
| 3173 | - $enroll = EnrollmentModel::do_enroll( $course_id, 0, $user_id ); | |
| 3174 | - if ( $enroll ) { | |
| 3175 | - wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); | |
| 2906 | + $enroll = tutor_utils()->do_enroll( $course_id, 0, $user_id ); | |
| 2907 | + if ( $enroll ) { | |
| 2908 | + wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); | |
| 2909 | + } else { | |
| 2910 | + wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); | |
| 2911 | + } | |
| 3176 | 2912 | } else { |
| 3177 | - wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); | |
| 2913 | + wp_send_json_error( __( 'Invalid course ID', 'tutor' ) ); | |
| 3178 | 2914 | } |
| 3179 | 2915 | } |
| 3180 | 2916 | |
| 3181 | 2917 | /** |
| @@ -3262,10 +2998,14 @@ | ||
| 3262 | 2998 | 'regular_price' => $regular_price, |
| 3263 | 2999 | 'sale_price' => $sale_price, |
| 3264 | 3000 | ); |
| 3265 | 3001 | |
| 3266 | - $card_data = apply_filters( 'tutor_course_mini_info', $info, $post ); | |
| 3002 | + if ( 'course-bundle' === $post->post_type && tutor_utils()->is_addon_enabled( 'tutor-pro/addons/course-bundle/course-bundle.php' ) ) { | |
| 3003 | + $info['total_course'] = count( BundleModel::get_bundle_course_ids( $post->ID ) ); | |
| 3004 | + } | |
| 3267 | 3005 | |
| 3006 | + $card_data = apply_filters( 'tutor_add_course_plan_info', $info, $post ); | |
| 3007 | + | |
| 3268 | 3008 | return $card_data; |
| 3269 | 3009 | } |
| 3270 | 3010 | |
| 3271 | 3011 | /** |
| @@ -3286,9 +3026,9 @@ | ||
| 3286 | 3026 | $info['last_updated'] = tutor_i18n_get_formated_date( $post->post_modified_at ); |
| 3287 | 3027 | $info['course_duration'] = tutor_utils()->get_course_duration( $post->ID, false ); |
| 3288 | 3028 | $info['total_enrolled'] = tutor_utils()->count_enrolled_users_by_course( $post->ID ); |
| 3289 | 3029 | |
| 3290 | - $card_data = apply_filters( 'tutor_course_card_data', $info, $post ); | |
| 3030 | + $card_data = apply_filters( 'tutor_add_course_plan_info', $info, $post ); | |
| 3291 | 3031 | |
| 3292 | 3032 | return $card_data; |
| 3293 | 3033 | } |
| 3294 | 3034 | |
| @@ -3327,217 +3067,6 @@ | ||
| 3327 | 3067 | $args['role__in'] = array_map( 'sanitize_text_field', $filter->role ); |
| 3328 | 3068 | } |
| 3329 | 3069 | |
| 3330 | 3070 | return $args; |
| 3331 | - } | |
| 3332 | - | |
| 3333 | - /** | |
| 3334 | - * Get a list of possible course status. | |
| 3335 | - * | |
| 3336 | - * @since 3.6.2 | |
| 3337 | - * | |
| 3338 | - * @return array | |
| 3339 | - */ | |
| 3340 | - public static function course_status_list() { | |
| 3341 | - return array( | |
| 3342 | - 'publish', | |
| 3343 | - 'private', | |
| 3344 | - 'draft', | |
| 3345 | - 'trash', | |
| 3346 | - 'pending', | |
| 3347 | - 'future', | |
| 3348 | - ); | |
| 3349 | - } | |
| 3350 | - | |
| 3351 | - /** | |
| 3352 | - * Link a course/bundle post to a WooCommerce product. | |
| 3353 | - * | |
| 3354 | - * @since 3.8.2 | |
| 3355 | - * | |
| 3356 | - * @param int $post_ID The WordPress post ID of the course. | |
| 3357 | - * @param int $product_id The WooCommerce product ID to associate with the course. | |
| 3358 | - * | |
| 3359 | - * @return void | |
| 3360 | - */ | |
| 3361 | - public static function sync_course_with_wc_product( $post_ID, $product_id ) { | |
| 3362 | - | |
| 3363 | - update_post_meta( $post_ID, self::COURSE_PRODUCT_ID_META, $product_id ); | |
| 3364 | - | |
| 3365 | - // Mark product for woocommerce. | |
| 3366 | - update_post_meta( $product_id, '_virtual', 'yes' ); | |
| 3367 | - update_post_meta( $product_id, '_tutor_product', 'yes' ); | |
| 3368 | - } | |
| 3369 | - | |
| 3370 | - /** | |
| 3371 | - * Map Tutor's course prices to WooCommerce. | |
| 3372 | - * | |
| 3373 | - * @since 3.8.2 | |
| 3374 | - * | |
| 3375 | - * @param int $post_ID The WordPress post ID of the course. | |
| 3376 | - * @param string|int|float $regular_price The regular price. | |
| 3377 | - * @param string|int|float $sale_price The sale price. | |
| 3378 | - * @return void | |
| 3379 | - */ | |
| 3380 | - private static function set_course_regular_and_sale_price( $post_ID, $regular_price, $sale_price ) { | |
| 3381 | - | |
| 3382 | - // Set course regular & sale price. | |
| 3383 | - update_post_meta( $post_ID, self::COURSE_PRICE_META, $regular_price ); | |
| 3384 | - update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $sale_price ); | |
| 3385 | - } | |
| 3386 | - | |
| 3387 | - /** | |
| 3388 | - * Render start/resume button in enrolled course action btn. | |
| 3389 | - * | |
| 3390 | - * @since 4.0.0 | |
| 3391 | - * | |
| 3392 | - * @param int $course_id course id. | |
| 3393 | - * | |
| 3394 | - * @return void | |
| 3395 | - */ | |
| 3396 | - public function render_course_action_btn( int $course_id ) { | |
| 3397 | - $is_completed = tutor_utils()->is_completed_course( $course_id ); | |
| 3398 | - if ( $is_completed ) { | |
| 3399 | - $certificate_addon_active = tutor_utils()->is_addon_enabled( 'tutor-certificate' ); | |
| 3400 | - if ( ! $certificate_addon_active ) { | |
| 3401 | - Button::make() | |
| 3402 | - ->tag( 'a' ) | |
| 3403 | - ->label( __( 'Completed', 'tutor' ) ) | |
| 3404 | - ->icon( Icon::COMPLETED_CIRCLE ) | |
| 3405 | - ->variant( Variant::PRIMARY ) | |
| 3406 | - ->size( Size::X_SMALL ) | |
| 3407 | - ->attr( 'href', esc_url( get_the_permalink( $course_id ) ) ) | |
| 3408 | - ->render(); | |
| 3409 | - } | |
| 3410 | - return; | |
| 3411 | - } | |
| 3412 | - | |
| 3413 | - $course_progress = tutor_utils()->get_course_completed_percent( $course_id, 0, true ); | |
| 3414 | - $button_text = $course_progress['completed_percent'] > 0 ? __( 'Resume', 'tutor' ) : __( 'Start', 'tutor' ); | |
| 3415 | - $button_url = tutor_utils()->get_course_first_lesson( $course_id ); | |
| 3416 | - | |
| 3417 | - if ( ! $button_url ) { | |
| 3418 | - $button_url = get_the_permalink( $course_id ); | |
| 3419 | - } | |
| 3420 | - | |
| 3421 | - Button::make() | |
| 3422 | - ->tag( 'a' ) | |
| 3423 | - ->label( $button_text ) | |
| 3424 | - ->icon( Icon::PLAY_2 ) | |
| 3425 | - ->variant( Variant::PRIMARY ) | |
| 3426 | - ->size( Size::X_SMALL ) | |
| 3427 | - ->attr( 'href', esc_url( $button_url ) ) | |
| 3428 | - ->render(); | |
| 3429 | - } | |
| 3430 | - | |
| 3431 | - /** | |
| 3432 | - * Get the content for the course completion modal. | |
| 3433 | - * | |
| 3434 | - * This method returns HTML for displaying a modal that informs the user | |
| 3435 | - * that they have not completed all required lessons and assessments. It includes | |
| 3436 | - * the user's current progress shown as a percentage and a progress bar. | |
| 3437 | - * | |
| 3438 | - * @since 4.0.0 | |
| 3439 | - * | |
| 3440 | - * @param float $course_progress The completion percentage of the course. | |
| 3441 | - * | |
| 3442 | - * @return string The rendered HTML content for the modal. | |
| 3443 | - */ | |
| 3444 | - public static function get_complete_modal_content( float $course_progress = 0 ): string { | |
| 3445 | - ob_start(); | |
| 3446 | - ?> | |
| 3447 | - <div> | |
| 3448 | - <p class="tutor-p3 tutor-text-secondary tutor-text-center tutor-mb-7 tutor-px-11"> | |
| 3449 | - <?php esc_html_e( 'You have not completed all required lessons and assessments. ', 'tutor' ); ?> | |
| 3450 | - </p> | |
| 3451 | - <div class="tutor-finish-course-progress tutor-border tutor-p-5 tutor-flex tutor-flex-column tutor-gap-4 tutor-surface-base tutor-rounded-md"> | |
| 3452 | - <div class="tutor-flex tutor-items-center tutor-justify-between"> | |
| 3453 | - <div class="tutor-p3 tutor-text-secondary"> | |
| 3454 | - <?php esc_html_e( 'Your Progress', 'tutor' ); ?> | |
| 3455 | - </div> | |
| 3456 | - <div class="tutor-p1 tutor-font-bold"> | |
| 3457 | - <?php echo esc_html( number_format_i18n( $course_progress ) . '%' ); ?> | |
| 3458 | - </div> | |
| 3459 | - </div> | |
| 3460 | - <?php | |
| 3461 | - Progress::make()->type( 'bar' )->value( $course_progress )->render(); | |
| 3462 | - ?> | |
| 3463 | - </div> | |
| 3464 | - </div> | |
| 3465 | - <?php | |
| 3466 | - return ob_get_clean(); | |
| 3467 | - } | |
| 3468 | - | |
| 3469 | - /** | |
| 3470 | - * Render the course complete button. | |
| 3471 | - * | |
| 3472 | - * Displays a button that allows the user to complete the course. If the course | |
| 3473 | - * progress is less than 100%, clicking the button shows a modal informing the user | |
| 3474 | - * that not all requirements are fulfilled. If the course progress is 100%, | |
| 3475 | - * clicking the button attempts to complete the course. | |
| 3476 | - * | |
| 3477 | - * @since 4.0.0 | |
| 3478 | - * | |
| 3479 | - * @param string $modal_id The HTML ID of the modal to display if not complete. | |
| 3480 | - * @param int $course_id The ID of the course. | |
| 3481 | - * @param float $course_progress The current completion percentage of the course. | |
| 3482 | - * @param string $size The button size. | |
| 3483 | - * @param string $tooltip Optional. Tooltip message. | |
| 3484 | - * @param bool $block Optional. Whether the button is full-width. | |
| 3485 | - * | |
| 3486 | - * @return void | |
| 3487 | - */ | |
| 3488 | - public static function render_course_complete_btn( string $modal_id, int $course_id, float $course_progress = 0, string $size = Size::MEDIUM, string $tooltip = '', bool $block = false ): void { | |
| 3489 | - $button = Button::make() | |
| 3490 | - ->variant( Variant::SECONDARY ) | |
| 3491 | - ->label( __( 'Complete the Course', 'tutor' ) ) | |
| 3492 | - ->icon( Icon::TICK_MARK ) | |
| 3493 | - ->size( $size ) | |
| 3494 | - ->block( $block ) | |
| 3495 | - ->attr( 'type', 'button' ); | |
| 3496 | - | |
| 3497 | - if ( ! empty( $tooltip ) ) { | |
| 3498 | - $button->disabled(); | |
| 3499 | - } | |
| 3500 | - | |
| 3501 | - if ( $course_progress < 100 ) { | |
| 3502 | - $button->attr( '@click', "TutorCore.modal.showModal('{$modal_id}')" ); | |
| 3503 | - } else { | |
| 3504 | - $button->attr( '@click', "handleCourseComplete({$course_id})" ); | |
| 3505 | - $button->attr( ':class', "courseCompleteMutation?.isPending ? 'tutor-btn-loading' : ''" ); | |
| 3506 | - $button->attr( ':disabled', 'courseCompleteMutation?.isPending' ); | |
| 3507 | - } | |
| 3508 | - | |
| 3509 | - if ( ! empty( $tooltip ) ) { | |
| 3510 | - Tooltip::make() | |
| 3511 | - ->content( $tooltip ) | |
| 3512 | - ->placement( Tooltip::PLACEMENT_BOTTOM ) | |
| 3513 | - ->arrow( Tooltip::ARROW_CENTER ) | |
| 3514 | - ->trigger_element( $button->get() ) | |
| 3515 | - ->render(); | |
| 3516 | - } else { | |
| 3517 | - $button->render(); | |
| 3518 | - } | |
| 3519 | - } | |
| 3520 | - | |
| 3521 | - /** | |
| 3522 | - * Render course retake button | |
| 3523 | - * | |
| 3524 | - * @since 4.0.0 | |
| 3525 | - * | |
| 3526 | - * @param string $modal_id Modal id. | |
| 3527 | - * @param string $size The button size. | |
| 3528 | - * @param bool $block Optional. Whether the button is full-width. | |
| 3529 | - * | |
| 3530 | - * @return void | |
| 3531 | - */ | |
| 3532 | - public static function render_course_retake_btn( string $modal_id, string $size = Size::MEDIUM, bool $block = false ): void { | |
| 3533 | - Button::make() | |
| 3534 | - ->variant( Variant::SECONDARY ) | |
| 3535 | - ->label( __( 'Retake this Course', 'tutor' ) ) | |
| 3536 | - ->icon( Icon::RELOAD_4 ) | |
| 3537 | - ->size( $size ) | |
| 3538 | - ->block( $block ) | |
| 3539 | - ->attr( 'type', 'button' ) | |
| 3540 | - ->attr( '@click', "TutorCore.modal.showModal('{$modal_id}')" ) | |
| 3541 | - ->render(); | |
| 3542 | 3071 | } |
| 3543 | 3072 | } |