| @@ -9,26 +9,22 @@ | ||
| 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; | |
| 17 | +use stdClass; | |
| 18 | +use TUTOR\Input; | |
| 19 | 19 | use Tutor\Ecommerce\Tax; |
| 20 | 20 | use Tutor\Models\QuizModel; |
| 21 | 21 | use Tutor\Helpers\HttpHelper; |
| 22 | 22 | use Tutor\Models\CourseModel; |
| 23 | -use Tutor\Components\Tooltip; | |
| 24 | 23 | use Tutor\Ecommerce\Ecommerce; |
| 25 | -use Tutor\Helpers\DateTimeHelper; | |
| 26 | 24 | use Tutor\Traits\JsonResponse; |
| 27 | 25 | use Tutor\Helpers\ValidationHelper; |
| 28 | -use Tutor\Models\EnrollmentModel; | |
| 29 | -use Tutor\Options_V2; | |
| 30 | -use TUTOR_ASSIGNMENTS\Assignments; | |
| 26 | +use TutorPro\CourseBundle\Models\BundleModel; | |
| 31 | 27 | |
| 32 | 28 | /** |
| 33 | 29 | * Course Class |
| 34 | 30 | * |
| @@ -98,15 +94,8 @@ | ||
| 98 | 94 | const PUBLIC_COURSE_META = '_tutor_is_public_course'; |
| 99 | 95 | const COURSE_SETTINGS_META = '_tutor_course_settings'; |
| 100 | 96 | const COURSE_LEVEL_META = '_tutor_course_level'; |
| 101 | 97 | |
| 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 | 98 | |
| 110 | 99 | /** |
| 111 | 100 | * Additional course meta info |
| 112 | 101 | * |
| @@ -160,8 +149,15 @@ | ||
| 160 | 149 | */ |
| 161 | 150 | add_action( 'save_tutor_course', array( $this, 'attach_product_with_course' ), 10, 2 ); |
| 162 | 151 | |
| 163 | 152 | /** |
| 153 | + * Add course level to course settings | |
| 154 | + * | |
| 155 | + * @since v.1.4.1 | |
| 156 | + */ | |
| 157 | + add_filter( 'tutor_course_settings_tabs', array( $this, 'add_course_level_to_settings' ) ); | |
| 158 | + | |
| 159 | + /** | |
| 164 | 160 | * Enable Disable Course Details Page Feature |
| 165 | 161 | * |
| 166 | 162 | * @since v.1.4.8 |
| 167 | 163 | */ |
| @@ -207,14 +203,9 @@ | ||
| 207 | 203 | * Delete course data after deleted course |
| 208 | 204 | * |
| 209 | 205 | * @since v.1.6.6 |
| 210 | 206 | */ |
| 211 | - add_action( | |
| 212 | - 'deleted_post', | |
| 213 | - function ( $post_id ) { | |
| 214 | - ( new CourseModel() )->delete_course_data( $post_id ); | |
| 215 | - } | |
| 216 | - ); | |
| 207 | + add_action( 'deleted_post', array( new CourseModel(), 'delete_course_data' ) ); | |
| 217 | 208 | |
| 218 | 209 | /** |
| 219 | 210 | * Delete course data after deleted course |
| 220 | 211 | * |
| @@ -307,13 +298,9 @@ | ||
| 307 | 298 | add_filter( 'tutor_user_list_args', array( $this, 'user_list_args_for_instructor' ) ); |
| 308 | 299 | |
| 309 | 300 | add_filter( 'template_include', array( $this, 'handle_password_protected' ) ); |
| 310 | 301 | add_action( 'login_form_postpass', array( $this, 'handle_password_submit' ) ); |
| 311 | - add_filter( 'post_password_required', array( $this, 'bypass_password_for_enrolled' ), 10, 2 ); | |
| 312 | 302 | add_filter( 'the_preview', array( $this, 'handle_schedule_courses' ) ); |
| 313 | - | |
| 314 | - add_action( 'tutor_course_action_btn', array( $this, 'render_course_action_btn' ) ); | |
| 315 | - add_action( 'wp_ajax_tutor_complete_course', array( $this, 'ajax_tutor_complete_course' ) ); | |
| 316 | 303 | } |
| 317 | 304 | |
| 318 | 305 | /** |
| 319 | 306 | * Handle schedule courses preview for instructors. |
| @@ -355,31 +342,8 @@ | ||
| 355 | 342 | } |
| 356 | 343 | } |
| 357 | 344 | |
| 358 | 345 | /** |
| 359 | - * Bypass password protection for enrolled users. | |
| 360 | - * | |
| 361 | - * @since 4.0.0 | |
| 362 | - * | |
| 363 | - * @param bool $required Whether the password is required. | |
| 364 | - * @param \WP_Post $post The post object. | |
| 365 | - * | |
| 366 | - * @return bool false if the current user is enrolled, original value otherwise. | |
| 367 | - */ | |
| 368 | - public function bypass_password_for_enrolled( $required, $post ) { | |
| 369 | - if ( ! $required ) { | |
| 370 | - return $required; | |
| 371 | - } | |
| 372 | - | |
| 373 | - $post_types = array( tutor()->course_post_type, tutor()->bundle_post_type ); | |
| 374 | - if ( in_array( $post->post_type, $post_types, true ) && EnrollmentModel::is_enrolled( $post->ID ) ) { | |
| 375 | - return false; | |
| 376 | - } | |
| 377 | - | |
| 378 | - return $required; | |
| 379 | - } | |
| 380 | - | |
| 381 | - /** | |
| 382 | 346 | * Handle password protected course/bundle. |
| 383 | 347 | * |
| 384 | 348 | * @since 3.0.0 |
| 385 | 349 | * |
| @@ -917,9 +881,9 @@ | ||
| 917 | 881 | |
| 918 | 882 | update_post_meta( $course_id, self::COURSE_PRICE_TYPE_META, self::PRICE_TYPE_FREE ); |
| 919 | 883 | |
| 920 | 884 | $link = admin_url( 'admin.php?page=create-course' ); |
| 921 | - if ( tutor()->has_pro && Input::post( 'from_dashboard', false, Input::TYPE_BOOL ) ) { | |
| 885 | + if ( Input::post( 'from_dashboard', false, Input::TYPE_BOOL ) ) { | |
| 922 | 886 | $link = tutor_utils()->tutor_dashboard_url( 'create-course' ); |
| 923 | 887 | } |
| 924 | 888 | |
| 925 | 889 | $link = add_query_arg( array( 'course_id' => $course_id ), $link ); |
| @@ -937,14 +901,15 @@ | ||
| 937 | 901 | * Get course list |
| 938 | 902 | * |
| 939 | 903 | * @since 3.0.0 |
| 940 | 904 | * |
| 941 | - * @since 3.2.0 Refactor the arguments & response as per new design. | |
| 905 | + * @since 3.2.0 | |
| 942 | 906 | * |
| 907 | + * Refactor the arguments & response as per new design | |
| 908 | + * | |
| 943 | 909 | * @return void |
| 944 | 910 | */ |
| 945 | 911 | public function ajax_course_list() { |
| 946 | - tutor_utils()->check_nonce(); | |
| 947 | 912 | $this->check_access(); |
| 948 | 913 | |
| 949 | 914 | $limit = Input::post( 'limit', 10, Input::TYPE_INT ); |
| 950 | 915 | $offset = Input::post( 'offset', 0, Input::TYPE_INT ); |
| @@ -964,9 +929,14 @@ | ||
| 964 | 929 | ); |
| 965 | 930 | |
| 966 | 931 | $exclude = Input::post( 'exclude', array(), Input::TYPE_ARRAY ); |
| 967 | 932 | if ( count( $exclude ) ) { |
| 968 | - $exclude = array_filter( $exclude, fn( $id ) => is_numeric( $id ) && $id > 0 ); | |
| 933 | + $exclude = array_filter( | |
| 934 | + $exclude, | |
| 935 | + function ( $id ) { | |
| 936 | + return is_numeric( $id ); | |
| 937 | + } | |
| 938 | + ); | |
| 969 | 939 | $args['post__not_in'] = $exclude; |
| 970 | 940 | } |
| 971 | 941 | |
| 972 | 942 | $courses = CourseModel::get_courses_by_args( $args ); |
| @@ -1102,17 +1072,12 @@ | ||
| 1102 | 1072 | 'course_benefits' => 'sanitize_textarea_field', |
| 1103 | 1073 | 'course_target_audience' => 'sanitize_textarea_field', |
| 1104 | 1074 | 'course_material_includes' => 'sanitize_textarea_field', |
| 1105 | 1075 | 'course_requirements' => 'sanitize_textarea_field', |
| 1106 | - ), | |
| 1107 | - true | |
| 1076 | + ) | |
| 1108 | 1077 | ); |
| 1109 | 1078 | |
| 1110 | 1079 | $course_id = (int) $params['course_id']; |
| 1111 | - if ( ! $course_id ) { | |
| 1112 | - $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); | |
| 1113 | - } | |
| 1114 | - | |
| 1115 | 1080 | $this->check_access( $course_id ); |
| 1116 | 1081 | |
| 1117 | 1082 | $errors = array(); |
| 1118 | 1083 | $validation = $this->validate_inputs( $params ); |
| @@ -1201,15 +1166,11 @@ | ||
| 1201 | 1166 | public function ajax_unlink_page_builder() { |
| 1202 | 1167 | tutor_utils()->check_nonce(); |
| 1203 | 1168 | |
| 1204 | 1169 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1205 | - if ( ! $course_id ) { | |
| 1206 | - $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); | |
| 1207 | - } | |
| 1208 | - | |
| 1170 | + $builder = Input::post( 'builder' ); | |
| 1209 | 1171 | $this->check_access( $course_id ); |
| 1210 | 1172 | |
| 1211 | - $builder = Input::post( 'builder' ); | |
| 1212 | 1173 | if ( 'elementor' === $builder ) { |
| 1213 | 1174 | delete_post_meta( $course_id, '_elementor_edit_mode' ); |
| 1214 | 1175 | } elseif ( 'droip' === $builder ) { |
| 1215 | 1176 | delete_post_meta( $course_id, 'droip_editor_mode' ); |
| @@ -1259,18 +1220,15 @@ | ||
| 1259 | 1220 | |
| 1260 | 1221 | $topic_contents = tutor_utils()->get_course_contents_by_topic( $post->ID, -1 ); |
| 1261 | 1222 | |
| 1262 | 1223 | if ( $topic_contents->have_posts() ) { |
| 1263 | - foreach ( $topic_contents->get_posts() as $content ) { | |
| 1264 | - if ( tutor()->quiz_post_type === $content->post_type ) { | |
| 1265 | - $questions = tutor_utils()->get_questions_by_quiz( $content->ID ); | |
| 1266 | - $content = (object) array_merge( | |
| 1267 | - (array) $content, | |
| 1268 | - array( 'total_question' => is_array( $questions ) ? count( $questions ) : 0 ) | |
| 1269 | - ); | |
| 1224 | + foreach ( $topic_contents->get_posts() as $post ) { | |
| 1225 | + if ( tutor()->quiz_post_type === $post->post_type ) { | |
| 1226 | + $questions = tutor_utils()->get_questions_by_quiz( $post->ID ); | |
| 1227 | + $post->total_question = is_array( $questions ) ? count( $questions ) : 0; | |
| 1270 | 1228 | } |
| 1271 | 1229 | |
| 1272 | - array_push( $current_topic['contents'], $content ); | |
| 1230 | + array_push( $current_topic['contents'], $post ); | |
| 1273 | 1231 | } |
| 1274 | 1232 | } |
| 1275 | 1233 | |
| 1276 | 1234 | $current_topic = apply_filters( 'tutor_filter_course_content', $current_topic ); |
| @@ -1285,23 +1243,20 @@ | ||
| 1285 | 1243 | /** |
| 1286 | 1244 | * Get course contents |
| 1287 | 1245 | * |
| 1288 | 1246 | * @since 3.0.0 |
| 1289 | - * | |
| 1290 | - * @return void | |
| 1291 | 1247 | */ |
| 1292 | 1248 | public function ajax_course_contents() { |
| 1293 | 1249 | tutor_utils()->check_nonce(); |
| 1294 | 1250 | |
| 1295 | - $errors = array(); | |
| 1296 | 1251 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1297 | 1252 | |
| 1298 | - if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1253 | + $this->check_access( $course_id ); | |
| 1254 | + | |
| 1255 | + if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1299 | 1256 | $errors['course_id'] = __( 'Invalid course id', 'tutor' ); |
| 1300 | 1257 | } |
| 1301 | 1258 | |
| 1302 | - $this->check_access( $course_id ); | |
| 1303 | - | |
| 1304 | 1259 | if ( ! empty( $errors ) ) { |
| 1305 | 1260 | $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); |
| 1306 | 1261 | } |
| 1307 | 1262 | |
| @@ -1325,14 +1280,14 @@ | ||
| 1325 | 1280 | |
| 1326 | 1281 | $errors = array(); |
| 1327 | 1282 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1328 | 1283 | |
| 1329 | - if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1284 | + $this->check_access( $course_id ); | |
| 1285 | + | |
| 1286 | + if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1330 | 1287 | $errors['course_id'] = __( 'Invalid course id', 'tutor' ); |
| 1331 | 1288 | } |
| 1332 | 1289 | |
| 1333 | - $this->check_access( $course_id ); | |
| 1334 | - | |
| 1335 | 1290 | if ( ! empty( $errors ) ) { |
| 1336 | 1291 | $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); |
| 1337 | 1292 | } |
| 1338 | 1293 | |
| @@ -1343,9 +1298,9 @@ | ||
| 1343 | 1298 | $price = 0; |
| 1344 | 1299 | $sale_price = 0; |
| 1345 | 1300 | $product_id = tutor_utils()->get_course_product_id( $course_id ); |
| 1346 | 1301 | |
| 1347 | - if ( WooCommerce::MONETIZE_BY === $monetize_by ) { | |
| 1302 | + if ( 'wc' === $monetize_by ) { | |
| 1348 | 1303 | $product = wc_get_product( $product_id ); |
| 1349 | 1304 | if ( $product ) { |
| 1350 | 1305 | $product_name = $product->get_name(); |
| 1351 | 1306 | $price = $product->get_regular_price(); |
| @@ -1352,9 +1307,9 @@ | ||
| 1352 | 1307 | $sale_price = $product->get_sale_price(); |
| 1353 | 1308 | } |
| 1354 | 1309 | } |
| 1355 | 1310 | |
| 1356 | - if ( Ecommerce::MONETIZE_BY === $monetize_by ) { | |
| 1311 | + if ( 'tutor' === $monetize_by ) { | |
| 1357 | 1312 | $price = get_post_meta( $course_id, self::COURSE_PRICE_META, true ); |
| 1358 | 1313 | $sale_price = get_post_meta( $course_id, self::COURSE_SALE_PRICE_META, true ); |
| 1359 | 1314 | } |
| 1360 | 1315 | |
| @@ -1369,12 +1324,12 @@ | ||
| 1369 | 1324 | $video_intro = get_post_meta( $course_id, '_video', true ); |
| 1370 | 1325 | if ( $video_intro ) { |
| 1371 | 1326 | $source = $video_intro['source'] ?? ''; |
| 1372 | 1327 | if ( 'html5' === $source ) { |
| 1373 | - $poster_url = wp_get_attachment_url( $video_intro['poster'] ?? 0 ); | |
| 1374 | - $source_html5 = wp_get_attachment_url( $video_intro['source_video_id'] ?? 0 ); | |
| 1375 | - $video_intro['poster_url'] = $poster_url; | |
| 1376 | - $video_intro['source_html5'] = $source_html5; | |
| 1328 | + $poster_url = wp_get_attachment_url( $video['poster'] ?? 0 ); | |
| 1329 | + $source_html5 = wp_get_attachment_url( $video['source_video_id'] ?? 0 ); | |
| 1330 | + $video['poster_url'] = $poster_url; | |
| 1331 | + $video['source_html5'] = $source_html5; | |
| 1377 | 1332 | } |
| 1378 | 1333 | } |
| 1379 | 1334 | |
| 1380 | 1335 | $course = get_post( $course_id, ARRAY_A ); |
| @@ -1383,23 +1338,13 @@ | ||
| 1383 | 1338 | } |
| 1384 | 1339 | |
| 1385 | 1340 | $editors = tutor_utils()->get_editor_list( $course_id ); |
| 1386 | 1341 | |
| 1387 | - /** | |
| 1388 | - * Replaced the post_author with current user id if post_author value is 0. | |
| 1389 | - * | |
| 1390 | - * @since 4.0.7 | |
| 1391 | - */ | |
| 1392 | - $post_author = (int) $course['post_author']; | |
| 1393 | - if ( 0 === $post_author && $course_id > 0 ) { | |
| 1394 | - $post_author = get_current_user_id(); | |
| 1395 | - } | |
| 1396 | - | |
| 1397 | 1342 | $data = array( |
| 1398 | 1343 | 'editors' => array_values( $editors ), |
| 1399 | 1344 | 'editor_used' => tutor_utils()->get_editor_used( $course_id ), |
| 1400 | 1345 | 'preview_link' => get_preview_post_link( $course_id ), |
| 1401 | - 'post_author' => tutor_utils()->get_tutor_user( $post_author ), | |
| 1346 | + 'post_author' => tutor_utils()->get_tutor_user( $course['post_author'] ), | |
| 1402 | 1347 | 'course_categories' => wp_get_post_terms( $course_id, CourseModel::COURSE_CATEGORY ), |
| 1403 | 1348 | 'course_tags' => wp_get_post_terms( $course_id, CourseModel::COURSE_TAG ), |
| 1404 | 1349 | 'thumbnail_id' => get_post_meta( $course_id, '_thumbnail_id', true ), |
| 1405 | 1350 | 'thumbnail' => get_the_post_thumbnail_url( $course_id ), |
| @@ -1539,9 +1484,9 @@ | ||
| 1539 | 1484 | $default_data = ( new Assets( false ) )->get_default_localized_data(); |
| 1540 | 1485 | |
| 1541 | 1486 | if ( isset( $default_data['current_user']['data']['id'] ) ) { |
| 1542 | 1487 | $tutor_user = tutor_utils()->get_tutor_user( $default_data['current_user']['data']['id'] ); |
| 1543 | - $default_data['current_user']['data']['tutor_profile_photo_url'] = is_object( $tutor_user ) ? $tutor_user->tutor_profile_photo_url : ''; | |
| 1488 | + $default_data['current_user']['data']['tutor_profile_photo_url'] = $tutor_user->tutor_profile_photo_url; | |
| 1544 | 1489 | } |
| 1545 | 1490 | |
| 1546 | 1491 | /** |
| 1547 | 1492 | * Localized only options to protect sensitive info like API keys. |
| @@ -1546,9 +1491,8 @@ | ||
| 1546 | 1491 | /** |
| 1547 | 1492 | * Localized only options to protect sensitive info like API keys. |
| 1548 | 1493 | */ |
| 1549 | 1494 | $required_options = array( |
| 1550 | - 'learning_mode', | |
| 1551 | 1495 | 'monetize_by', |
| 1552 | 1496 | 'enable_course_marketplace', |
| 1553 | 1497 | 'course_permalink_base', |
| 1554 | 1498 | 'supported_video_sources', |
| @@ -1768,13 +1712,13 @@ | ||
| 1768 | 1712 | $topic_id = (int) isset( $sorting_order[0], $sorting_order[0]['topic_id'] ) ? $sorting_order[0]['topic_id'] : 0; |
| 1769 | 1713 | $course_id = wp_get_post_parent_id( $topic_id ); |
| 1770 | 1714 | |
| 1771 | 1715 | if ( ! $topic_id || ! $course_id ) { |
| 1772 | - $this->response_bad_request( tutor_utils()->error_message( 'invalid_req' ) ); | |
| 1716 | + wp_send_json_error( tutor_utils()->error_message( 'invalid_req' ) ); | |
| 1773 | 1717 | } |
| 1774 | 1718 | |
| 1775 | - if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) && ! User::is_admin() ) { | |
| 1776 | - $this->json_response( tutor_utils()->error_message(), null, HttpHelper::STATUS_UNAUTHORIZED ); | |
| 1719 | + if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) || ! User::is_admin() ) { | |
| 1720 | + wp_send_json_error( tutor_utils()->error_message() ); | |
| 1777 | 1721 | } |
| 1778 | 1722 | |
| 1779 | 1723 | if ( Input::has( 'content_parent' ) ) { |
| 1780 | 1724 | $content_parent = Input::post( 'content_parent', array(), Input::TYPE_ARRAY ); |
| @@ -1788,9 +1732,9 @@ | ||
| 1788 | 1732 | |
| 1789 | 1733 | // Save course content order. |
| 1790 | 1734 | $this->save_course_content_order( $sorting_order ); |
| 1791 | 1735 | |
| 1792 | - $this->response_success( __( 'Course content order updated successfully!', 'tutor' ) ); | |
| 1736 | + wp_send_json_success(); | |
| 1793 | 1737 | } |
| 1794 | 1738 | |
| 1795 | 1739 | /** |
| 1796 | 1740 | * Restrict new student entry |
| @@ -1815,11 +1759,9 @@ | ||
| 1815 | 1759 | /** |
| 1816 | 1760 | * Restrict media |
| 1817 | 1761 | * |
| 1818 | 1762 | * @since 1.0.0 |
| 1819 | - * | |
| 1820 | 1763 | * @param string $where where clause. |
| 1821 | - * | |
| 1822 | 1764 | * @return string |
| 1823 | 1765 | */ |
| 1824 | 1766 | public function restrict_media( $where ) { |
| 1825 | 1767 | $action = Input::post( 'action' ); |
| @@ -1850,9 +1792,9 @@ | ||
| 1850 | 1792 | } |
| 1851 | 1793 | |
| 1852 | 1794 | $i = 0; |
| 1853 | 1795 | foreach ( $sort_order as $topic ) { |
| 1854 | - ++$i; | |
| 1796 | + $i++; | |
| 1855 | 1797 | $wpdb->update( |
| 1856 | 1798 | $wpdb->posts, |
| 1857 | 1799 | array( 'menu_order' => $i ), |
| 1858 | 1800 | array( 'ID' => $topic['topic_id'] ) |
| @@ -1975,9 +1917,8 @@ | ||
| 1975 | 1917 | 'source_embedded' => 'wp_kses_post', |
| 1976 | 1918 | ), |
| 1977 | 1919 | true |
| 1978 | 1920 | ); |
| 1979 | - $video = tutor_utils()->filter_video_meta( $video ); | |
| 1980 | 1921 | $video_source = tutor_utils()->array_get( 'source', $video ); |
| 1981 | 1922 | if ( -1 !== $video_source ) { |
| 1982 | 1923 | update_post_meta( $post_ID, '_video', $video ); |
| 1983 | 1924 | } elseif ( ! tutor_is_rest() ) { |
| @@ -1987,23 +1928,12 @@ | ||
| 1987 | 1928 | |
| 1988 | 1929 | /** |
| 1989 | 1930 | * Adding author to instructor automatically |
| 1990 | 1931 | */ |
| 1991 | - $requested_author_id = Input::post( 'post_author_override', 0, Input::TYPE_INT ); | |
| 1992 | 1932 | |
| 1993 | - /** | |
| 1994 | - * Only accept the requested author override if it targets a real,approved instructor or admin | |
| 1995 | - * | |
| 1996 | - * @since 4.0.4 | |
| 1997 | - */ | |
| 1998 | - $author_id = $post->post_author; | |
| 1999 | - if ( $requested_author_id && ( User::is_admin() || User::is_instructor() ) ) { | |
| 2000 | - if ( User::is_admin( $requested_author_id ) || User::is_instructor( $requested_author_id, true ) ) { | |
| 2001 | - $author_id = $requested_author_id; | |
| 2002 | - } | |
| 2003 | - } | |
| 2004 | - | |
| 2005 | - $attached = (int) $wpdb->get_var( | |
| 1933 | + // Override post author id. | |
| 1934 | + $author_id = isset( $_POST['post_author_override'] ) ? $_POST['post_author_override'] : $post->post_author; //phpcs:ignore | |
| 1935 | + $attached = (int) $wpdb->get_var( | |
| 2006 | 1936 | $wpdb->prepare( |
| 2007 | 1937 | "SELECT COUNT(umeta_id) FROM {$wpdb->usermeta} |
| 2008 | 1938 | WHERE user_id = %d |
| 2009 | 1939 | AND meta_key = '_tutor_instructor_course_id' |
| @@ -2028,20 +1958,8 @@ | ||
| 2028 | 1958 | update_post_meta( $post_ID, $key, ( isset( $_POST[ $key ] ) ? 'yes' : 'no' ) ); |
| 2029 | 1959 | } |
| 2030 | 1960 | } |
| 2031 | 1961 | |
| 2032 | - /** | |
| 2033 | - * Update course content if main author is changed and multi-instructor addon is disabled. | |
| 2034 | - * | |
| 2035 | - * @since 4.0.4 | |
| 2036 | - */ | |
| 2037 | - if ( ! $attached && ! tutor_utils()->is_addon_enabled( 'tutor-multi-instructors' ) ) { | |
| 2038 | - CourseModel::update_course_content_author( $post_ID, (int) $author_id ); | |
| 2039 | - // Remove all existing instructors from the course and add the new one. | |
| 2040 | - delete_metadata( 'user', 0, '_tutor_instructor_course_id', $post_ID, true ); | |
| 2041 | - add_user_meta( $author_id, '_tutor_instructor_course_id', $post_ID ); | |
| 2042 | - } | |
| 2043 | - | |
| 2044 | 1962 | do_action( 'tutor_save_course_after', $post_ID, $post ); |
| 2045 | 1963 | } |
| 2046 | 1964 | |
| 2047 | 1965 | /** |
| @@ -2200,9 +2118,9 @@ | ||
| 2200 | 2118 | // Process purchase. |
| 2201 | 2119 | |
| 2202 | 2120 | } else { |
| 2203 | 2121 | // Free enroll. |
| 2204 | - EnrollmentModel::do_enroll( $course_id ); | |
| 2122 | + tutor_utils()->do_enroll( $course_id ); | |
| 2205 | 2123 | } |
| 2206 | 2124 | |
| 2207 | 2125 | $referer_url = wp_get_referer(); |
| 2208 | 2126 | wp_safe_redirect( tutor_utils()->get_nocache_url( $referer_url ) ); |
| @@ -2236,9 +2154,9 @@ | ||
| 2236 | 2154 | if ( ! $user_id ) { |
| 2237 | 2155 | die( esc_html__( 'Please Sign-In', 'tutor' ) ); |
| 2238 | 2156 | } |
| 2239 | 2157 | |
| 2240 | - if ( ! EnrollmentModel::is_enrolled( $course_id, $user_id ) ) { | |
| 2158 | + if ( ! tutor_utils()->is_enrolled( $course_id, $user_id ) ) { | |
| 2241 | 2159 | die( esc_html__( 'User is not enrolled in course', 'tutor' ) ); |
| 2242 | 2160 | } |
| 2243 | 2161 | |
| 2244 | 2162 | /** |
| @@ -2245,18 +2163,16 @@ | ||
| 2245 | 2163 | * Filter hook provided to restrict course completion. This is useful |
| 2246 | 2164 | * for specific cases like prerequisites. WP_Error should be returned |
| 2247 | 2165 | * from the filter value to prevent the completion. |
| 2248 | 2166 | */ |
| 2249 | - $can_complete = apply_filters( 'tutor_user_can_complete_course', CourseModel::can_complete_course( $course_id, $user_id ), $user_id, $course_id ); | |
| 2167 | + $can_complete = apply_filters( 'tutor_user_can_complete_course', true, $user_id, $course_id ); | |
| 2250 | 2168 | |
| 2251 | 2169 | if ( is_wp_error( $can_complete ) ) { |
| 2252 | 2170 | tutor_utils()->redirect_to( $permalink, $can_complete->get_error_message(), 'error' ); |
| 2253 | - } elseif ( ! $can_complete ) { | |
| 2254 | - tutor_utils()->redirect_to( $permalink, __( 'You do not have permission to complete this course.', 'tutor' ), 'error' ); | |
| 2255 | 2171 | } else { |
| 2256 | 2172 | CourseModel::mark_course_as_completed( $course_id, $user_id ); |
| 2257 | 2173 | // Set temporary identifier to show review pop up. |
| 2258 | - self::set_review_popup_data( $user_id, $course_id ); | |
| 2174 | + self::set_review_popup_data( $user_id, $course_id, $permalink ); | |
| 2259 | 2175 | |
| 2260 | 2176 | wp_safe_redirect( $permalink ); |
| 2261 | 2177 | exit; |
| 2262 | 2178 | } |
| @@ -2262,52 +2178,8 @@ | ||
| 2262 | 2178 | } |
| 2263 | 2179 | } |
| 2264 | 2180 | |
| 2265 | 2181 | /** |
| 2266 | - * Ajax course complete handler | |
| 2267 | - * | |
| 2268 | - * @since 4.0.0 | |
| 2269 | - * | |
| 2270 | - * @return void | |
| 2271 | - */ | |
| 2272 | - public function ajax_tutor_complete_course() { | |
| 2273 | - $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); | |
| 2274 | - | |
| 2275 | - // Checking nonce. | |
| 2276 | - if ( ! tutor_utils()->is_nonce_verified() ) { | |
| 2277 | - $this->response_bad_request( tutor_utils()->error_message( 'nonce' ) ); | |
| 2278 | - } | |
| 2279 | - | |
| 2280 | - if ( ! $course_id ) { | |
| 2281 | - $this->response_bad_request( __( 'Invalid course', 'tutor' ) ); | |
| 2282 | - } | |
| 2283 | - | |
| 2284 | - $user_id = get_current_user_id(); | |
| 2285 | - if ( ! EnrollmentModel::is_enrolled( $course_id, $user_id ) ) { | |
| 2286 | - $this->response_bad_request( __( 'You are not enrolled in this course', 'tutor' ) ); | |
| 2287 | - } | |
| 2288 | - | |
| 2289 | - /** | |
| 2290 | - * Filter hook provided to restrict course completion. This is useful | |
| 2291 | - * for specific cases like prerequisites. WP_Error should be returned | |
| 2292 | - * from the filter value to prevent the completion. | |
| 2293 | - */ | |
| 2294 | - $can_complete = apply_filters( 'tutor_user_can_complete_course', CourseModel::can_complete_course( $course_id, $user_id ), $user_id, $course_id ); | |
| 2295 | - | |
| 2296 | - if ( is_wp_error( $can_complete ) ) { | |
| 2297 | - $this->response_bad_request( $can_complete->get_error_message() ); | |
| 2298 | - } elseif ( ! $can_complete ) { | |
| 2299 | - $this->response_bad_request( __( 'You do not have permission to complete this course.', 'tutor' ) ); | |
| 2300 | - } else { | |
| 2301 | - CourseModel::mark_course_as_completed( $course_id, $user_id ); | |
| 2302 | - // Set temporary identifier to show review pop up. | |
| 2303 | - self::set_review_popup_data( $user_id, $course_id ); | |
| 2304 | - | |
| 2305 | - $this->response_success( __( 'Course completed successfully', 'tutor' ) ); | |
| 2306 | - } | |
| 2307 | - } | |
| 2308 | - | |
| 2309 | - /** | |
| 2310 | 2182 | * Set data for review popup. |
| 2311 | 2183 | * |
| 2312 | 2184 | * @since 2.2.5 |
| 2313 | 2185 | * @since 2.4.0 removed $permalink param. store user meta instead of option data. |
| @@ -2333,34 +2205,18 @@ | ||
| 2333 | 2205 | * @since 1.0.0 |
| 2334 | 2206 | * @return void |
| 2335 | 2207 | */ |
| 2336 | 2208 | public function popup_review_form() { |
| 2337 | - if ( ! is_user_logged_in() ) { | |
| 2338 | - return; | |
| 2339 | - } | |
| 2209 | + if ( is_user_logged_in() ) { | |
| 2210 | + $user_id = get_current_user_id(); | |
| 2211 | + $course_id = get_the_ID(); | |
| 2212 | + $meta_key = User::get_review_popup_meta( $course_id ); | |
| 2213 | + $review_course_id = (int) get_user_meta( $user_id, $meta_key, true ); | |
| 2340 | 2214 | |
| 2341 | - $is_learning_area = tutor_utils()->is_learning_area(); | |
| 2342 | - $is_legacy_learning = tutor_utils()->is_legacy_learning_mode(); | |
| 2343 | - if ( $is_legacy_learning && $is_learning_area ) { | |
| 2344 | - return; | |
| 2215 | + if ( is_single() && $course_id === $review_course_id ) { | |
| 2216 | + include tutor()->path . 'views/modal/review.php'; | |
| 2217 | + } | |
| 2345 | 2218 | } |
| 2346 | - | |
| 2347 | - $course_id = get_the_ID(); | |
| 2348 | - | |
| 2349 | - if ( $is_learning_area && ! Input::has( 'subpage' ) ) { | |
| 2350 | - $course_id = wp_get_post_parent_id( wp_get_post_parent_id( get_the_ID() ) ); | |
| 2351 | - } | |
| 2352 | - | |
| 2353 | - if ( empty( $course_id ) ) { | |
| 2354 | - return; | |
| 2355 | - } | |
| 2356 | - | |
| 2357 | - $user_id = get_current_user_id(); | |
| 2358 | - $meta_key = User::get_review_popup_meta( $course_id ); | |
| 2359 | - $review_course_id = (int) get_user_meta( $user_id, $meta_key, true ); | |
| 2360 | - if ( is_single() && $course_id === $review_course_id ) { | |
| 2361 | - include tutor()->path . 'views/modal/review.php'; | |
| 2362 | - } | |
| 2363 | 2219 | } |
| 2364 | 2220 | |
| 2365 | 2221 | /** |
| 2366 | 2222 | * Review popup data clear |
| @@ -2388,9 +2244,8 @@ | ||
| 2388 | 2244 | /** |
| 2389 | 2245 | * Delete course delete from frontend dashboard |
| 2390 | 2246 | * |
| 2391 | 2247 | * @since 2.0.0 |
| 2392 | - * | |
| 2393 | 2248 | * @return void |
| 2394 | 2249 | */ |
| 2395 | 2250 | public function tutor_delete_dashboard_course() { |
| 2396 | 2251 | tutor_utils()->checking_nonce(); |
| @@ -2409,9 +2264,9 @@ | ||
| 2409 | 2264 | wp_send_json_error( array( 'message' => __( 'Only main instructor can delete this course', 'tutor' ) ) ); |
| 2410 | 2265 | } |
| 2411 | 2266 | |
| 2412 | 2267 | // Check if user is only an instructor. |
| 2413 | - if ( ! User::is_admin() ) { | |
| 2268 | + if ( ! current_user_can( 'administrator' ) ) { | |
| 2414 | 2269 | // Check if instructor can trash course. |
| 2415 | 2270 | $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ); |
| 2416 | 2271 | |
| 2417 | 2272 | if ( ! $can_trash_post ) { |
| @@ -2421,9 +2276,9 @@ | ||
| 2421 | 2276 | |
| 2422 | 2277 | $trash_course = wp_update_post( |
| 2423 | 2278 | array( |
| 2424 | 2279 | 'ID' => $course_id, |
| 2425 | - 'post_status' => CourseModel::STATUS_TRASH, | |
| 2280 | + 'post_status' => 'trash', | |
| 2426 | 2281 | ) |
| 2427 | 2282 | ); |
| 2428 | 2283 | |
| 2429 | 2284 | if ( $trash_course ) { |
| @@ -2598,8 +2453,33 @@ | ||
| 2598 | 2453 | } |
| 2599 | 2454 | } |
| 2600 | 2455 | |
| 2601 | 2456 | /** |
| 2457 | + * Add Course level to course settings | |
| 2458 | + * | |
| 2459 | + * @since 1.4.1 | |
| 2460 | + * | |
| 2461 | + * @param array $args arguments. | |
| 2462 | + * @return array | |
| 2463 | + */ | |
| 2464 | + public function add_course_level_to_settings( $args ) { | |
| 2465 | + $course_id = get_the_ID(); | |
| 2466 | + $levels = tutor_utils()->course_levels(); | |
| 2467 | + $course_level = get_post_meta( $course_id, '_tutor_course_level', true ); | |
| 2468 | + | |
| 2469 | + $args['general']['fields']['_tutor_course_level'] = array( | |
| 2470 | + 'type' => 'select', | |
| 2471 | + 'label' => __( 'Difficulty Level', 'tutor' ), | |
| 2472 | + 'label_title' => __( 'Enable', 'tutor' ), | |
| 2473 | + 'options' => $levels, | |
| 2474 | + 'value' => $course_level ? $course_level : 'intermediate', | |
| 2475 | + 'desc' => __( 'Course difficulty level', 'tutor' ), | |
| 2476 | + ); | |
| 2477 | + | |
| 2478 | + return $args; | |
| 2479 | + } | |
| 2480 | + | |
| 2481 | + /** | |
| 2602 | 2482 | * Check if course starting |
| 2603 | 2483 | * |
| 2604 | 2484 | * @since 1.4.8 |
| 2605 | 2485 | * @return void |
| @@ -2619,9 +2499,8 @@ | ||
| 2619 | 2499 | /** |
| 2620 | 2500 | * Add Course level to course settings |
| 2621 | 2501 | * |
| 2622 | 2502 | * @since 1.4.8 |
| 2623 | - * | |
| 2624 | 2503 | * @return void |
| 2625 | 2504 | */ |
| 2626 | 2505 | public function course_elements_enable_disable() { |
| 2627 | 2506 | add_filter( 'tutor_course/single/completing-progress-bar', array( $this, 'enable_disable_course_progress_bar' ) ); |
| @@ -2654,13 +2533,12 @@ | ||
| 2654 | 2533 | * |
| 2655 | 2534 | * @since 1.4.8 |
| 2656 | 2535 | * |
| 2657 | 2536 | * @param string $html HTML string. |
| 2658 | - * | |
| 2659 | 2537 | * @return string |
| 2660 | 2538 | */ |
| 2661 | 2539 | public function enable_disable_material_includes( $html ) { |
| 2662 | - $disable_option = ! (bool) get_tutor_option( 'enable_course_material', true ); | |
| 2540 | + $disable_option = ! (bool) get_tutor_option( 'enable_course_material', true, true ); | |
| 2663 | 2541 | if ( $disable_option ) { |
| 2664 | 2542 | return ''; |
| 2665 | 2543 | } |
| 2666 | 2544 | return $html; |
| @@ -2671,9 +2549,8 @@ | ||
| 2671 | 2549 | * |
| 2672 | 2550 | * @since 1.4.8 |
| 2673 | 2551 | * |
| 2674 | 2552 | * @param string $html HTML string. |
| 2675 | - * | |
| 2676 | 2553 | * @return string |
| 2677 | 2554 | */ |
| 2678 | 2555 | public function enable_disable_course_content( $html ) { |
| 2679 | 2556 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_description', true, true ); |
| @@ -2688,9 +2565,8 @@ | ||
| 2688 | 2565 | * |
| 2689 | 2566 | * @since 1.4.8 |
| 2690 | 2567 | * |
| 2691 | 2568 | * @param string $html HTML string. |
| 2692 | - * | |
| 2693 | 2569 | * @return string |
| 2694 | 2570 | */ |
| 2695 | 2571 | public function enable_disable_course_benefits( $html ) { |
| 2696 | 2572 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_benefits', true, true ); |
| @@ -2705,9 +2581,8 @@ | ||
| 2705 | 2581 | * |
| 2706 | 2582 | * @since 1.4.8 |
| 2707 | 2583 | * |
| 2708 | 2584 | * @param string $html HTML string. |
| 2709 | - * | |
| 2710 | 2585 | * @return string |
| 2711 | 2586 | */ |
| 2712 | 2587 | public function enable_disable_course_requirements( $html ) { |
| 2713 | 2588 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_requirements', true, true ); |
| @@ -2722,9 +2597,8 @@ | ||
| 2722 | 2597 | * |
| 2723 | 2598 | * @since 1.4.8 |
| 2724 | 2599 | * |
| 2725 | 2600 | * @param string $html HTML string. |
| 2726 | - * | |
| 2727 | 2601 | * @return string |
| 2728 | 2602 | */ |
| 2729 | 2603 | public function enable_disable_course_target_audience( $html ) { |
| 2730 | 2604 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_target_audience', true, true ); |
| @@ -2769,9 +2643,9 @@ | ||
| 2769 | 2643 | unset( $items['reviews'] ); |
| 2770 | 2644 | } |
| 2771 | 2645 | |
| 2772 | 2646 | // Whether enrollment require. |
| 2773 | - $is_enrolled = EnrollmentModel::is_enrolled(); | |
| 2647 | + $is_enrolled = tutor_utils()->is_enrolled(); | |
| 2774 | 2648 | |
| 2775 | 2649 | return array_filter( |
| 2776 | 2650 | $items, |
| 2777 | 2651 | function ( $item ) use ( $is_enrolled ) { |
| @@ -2786,9 +2660,8 @@ | ||
| 2786 | 2660 | /** |
| 2787 | 2661 | * Filter product in shop page |
| 2788 | 2662 | * |
| 2789 | 2663 | * @since 1.4.9 |
| 2790 | - * | |
| 2791 | 2664 | * @return void|null |
| 2792 | 2665 | */ |
| 2793 | 2666 | public function filter_product_in_shop_page() { |
| 2794 | 2667 | $hide_course_from_shop_page = (bool) get_tutor_option( 'hide_course_from_shop_page' ); |
| @@ -2794,11 +2667,11 @@ | ||
| 2794 | 2667 | $hide_course_from_shop_page = (bool) get_tutor_option( 'hide_course_from_shop_page' ); |
| 2795 | 2668 | if ( ! $hide_course_from_shop_page ) { |
| 2796 | 2669 | return; |
| 2797 | 2670 | } |
| 2798 | - add_filter( 'woocommerce_product_query', array( $this, 'filter_woocommerce_product_query' ) ); | |
| 2799 | - add_filter( 'edd_downloads_query', array( $this, 'filter_edd_downloads_query' ), 10 ); | |
| 2800 | - add_filter( 'pre_get_posts', array( $this, 'filter_archive_meta_query' ), 1 ); | |
| 2671 | + add_action( 'woocommerce_product_query', array( $this, 'filter_woocommerce_product_query' ) ); | |
| 2672 | + add_filter( 'edd_downloads_query', array( $this, 'filter_edd_downloads_query' ), 10, 2 ); | |
| 2673 | + add_action( 'pre_get_posts', array( $this, 'filter_archive_meta_query' ), 1 ); | |
| 2801 | 2674 | } |
| 2802 | 2675 | |
| 2803 | 2676 | |
| 2804 | 2677 | /** |
| @@ -2804,9 +2677,8 @@ | ||
| 2804 | 2677 | /** |
| 2805 | 2678 | * Tutor product meta query |
| 2806 | 2679 | * |
| 2807 | 2680 | * @since 1.4.9 |
| 2808 | - * | |
| 2809 | 2681 | * @return array |
| 2810 | 2682 | */ |
| 2811 | 2683 | public function tutor_product_meta_query() { |
| 2812 | 2684 | $meta_query = array( |
| @@ -2821,9 +2693,8 @@ | ||
| 2821 | 2693 | * |
| 2822 | 2694 | * @since 1.4.9 |
| 2823 | 2695 | * |
| 2824 | 2696 | * @param \WP_Query $wp_query WP Query instance. |
| 2825 | - * | |
| 2826 | 2697 | * @return \WP_Query |
| 2827 | 2698 | */ |
| 2828 | 2699 | public function filter_woocommerce_product_query( $wp_query ) { |
| 2829 | 2700 | $product_ids = $this->get_connected_wc_product_ids(); |
| @@ -2865,9 +2736,8 @@ | ||
| 2865 | 2736 | * |
| 2866 | 2737 | * @since 1.4.9 |
| 2867 | 2738 | * |
| 2868 | 2739 | * @param \WP_Query $query WP Query instance. |
| 2869 | - * | |
| 2870 | 2740 | * @return \WP_Query |
| 2871 | 2741 | */ |
| 2872 | 2742 | public function filter_edd_downloads_query( $query ) { |
| 2873 | 2743 | $query['meta_query'][] = $this->tutor_product_meta_query(); |
| @@ -2879,9 +2749,8 @@ | ||
| 2879 | 2749 | * |
| 2880 | 2750 | * @since 1.4.9 |
| 2881 | 2751 | * |
| 2882 | 2752 | * @param \WP_Query $wp_query WP Query instance. |
| 2883 | - * | |
| 2884 | 2753 | * @return \WP_Query |
| 2885 | 2754 | */ |
| 2886 | 2755 | public function filter_archive_meta_query( $wp_query ) { |
| 2887 | 2756 | if ( ! is_admin() && $wp_query->is_archive && $wp_query->get( 'post_type' ) === 'download' ) { |
| @@ -2895,9 +2764,8 @@ | ||
| 2895 | 2764 | * |
| 2896 | 2765 | * @since 1.5.8 |
| 2897 | 2766 | * |
| 2898 | 2767 | * @param string $html HTML string. |
| 2899 | - * | |
| 2900 | 2768 | * @return string |
| 2901 | 2769 | */ |
| 2902 | 2770 | public function remove_price_if_enrolled( $html ) { |
| 2903 | 2771 | $should_removed = apply_filters( 'should_remove_price_if_enrolled', true ); |
| @@ -2903,9 +2771,9 @@ | ||
| 2903 | 2771 | $should_removed = apply_filters( 'should_remove_price_if_enrolled', true ); |
| 2904 | 2772 | |
| 2905 | 2773 | if ( $should_removed ) { |
| 2906 | 2774 | $course_id = get_the_ID(); |
| 2907 | - $enrolled = EnrollmentModel::is_enrolled( $course_id ); | |
| 2775 | + $enrolled = tutor_utils()->is_enrolled( $course_id ); | |
| 2908 | 2776 | if ( $enrolled ) { |
| 2909 | 2777 | $html = ''; |
| 2910 | 2778 | } |
| 2911 | 2779 | } |
| @@ -2912,119 +2780,118 @@ | ||
| 2912 | 2780 | return $html; |
| 2913 | 2781 | } |
| 2914 | 2782 | |
| 2915 | 2783 | /** |
| 2916 | - * Get course completion missing requirements message. | |
| 2784 | + * Check if all lessons and quizzes done before mark course complete. | |
| 2917 | 2785 | * |
| 2918 | - * @since 4.0.0 | |
| 2786 | + * @since 1.5.8 | |
| 2919 | 2787 | * |
| 2920 | - * @param int $course_id Course ID. | |
| 2921 | - * @param int $user_id User ID. | |
| 2922 | - * | |
| 2923 | - * @return string|null | |
| 2788 | + * @param string $html HTML string. | |
| 2789 | + * @return string | |
| 2924 | 2790 | */ |
| 2925 | - public static function get_course_completion_restrict_msg( $course_id = 0, $user_id = 0 ) { | |
| 2926 | - $course_id = tutor_utils()->get_post_id( $course_id ); | |
| 2927 | - $user_id = tutor_utils()->get_user_id( $user_id ); | |
| 2791 | + public function tutor_lms_hide_course_complete_btn( $html ) { | |
| 2928 | 2792 | |
| 2929 | - if ( 'strict' !== tutor_utils()->get_option( 'course_completion_process' ) ) { | |
| 2930 | - return null; | |
| 2793 | + $completion_mode = tutor_utils()->get_option( 'course_completion_process' ); | |
| 2794 | + if ( 'strict' !== $completion_mode ) { | |
| 2795 | + return $html; | |
| 2931 | 2796 | } |
| 2932 | 2797 | |
| 2933 | - $completed_lessons = tutor_utils()->get_completed_lesson_count_by_course( $course_id, $user_id ); | |
| 2934 | - $total_lessons = tutor_utils()->get_lesson_count_by_course( $course_id ); // @phpstan-ignore method.notFound | |
| 2798 | + $completed_lesson = tutor_utils()->get_completed_lesson_count_by_course(); | |
| 2799 | + $lesson_count = tutor_utils()->get_lesson_count_by_course(); | |
| 2935 | 2800 | |
| 2936 | - if ( $completed_lessons < $total_lessons ) { | |
| 2937 | - return __( 'Complete all lessons to mark this course as complete', 'tutor' ); | |
| 2801 | + if ( $completed_lesson < $lesson_count ) { | |
| 2802 | + return '<div class="tutor-alert tutor-warning tutor-mt-28"> | |
| 2803 | + <div class="tutor-alert-text"> | |
| 2804 | + <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span> | |
| 2805 | + <span>' . __( 'Complete all lessons to mark this course as complete', 'tutor' ) . '</span> | |
| 2806 | + </div> | |
| 2807 | + </div>'; | |
| 2938 | 2808 | } |
| 2939 | 2809 | |
| 2940 | - $course_contents = tutor_utils()->get_course_contents_by_id( $course_id ); | |
| 2810 | + $quizzes = array(); | |
| 2811 | + $assignments = array(); | |
| 2941 | 2812 | |
| 2942 | - $required_quiz_pass_count = 0; | |
| 2943 | - $required_assignment_pass_count = 0; | |
| 2813 | + $course_contents = tutor_utils()->get_course_contents_by_id(); | |
| 2814 | + if ( tutor_utils()->count( $course_contents ) ) { | |
| 2815 | + foreach ( $course_contents as $content ) { | |
| 2816 | + if ( 'tutor_quiz' === $content->post_type ) { | |
| 2817 | + $quizzes[] = $content; | |
| 2818 | + } | |
| 2819 | + if ( 'tutor_assignments' === $content->post_type ) { | |
| 2820 | + $assignments[] = $content; | |
| 2821 | + } | |
| 2822 | + } | |
| 2823 | + } | |
| 2944 | 2824 | |
| 2945 | - $is_assignment_addon_enabled = tutor_utils()->is_addon_enabled( 'tutor-assignments' ); | |
| 2825 | + $required_assignment_pass = 0; | |
| 2946 | 2826 | |
| 2947 | - foreach ( $course_contents as $content ) { | |
| 2827 | + foreach ( $assignments as $row ) { | |
| 2948 | 2828 | |
| 2949 | - if ( tutor()->quiz_post_type === $content->post_type ) { | |
| 2950 | - if ( ! QuizModel::is_quiz_passed( $content->ID, $user_id ) ) { | |
| 2951 | - ++$required_quiz_pass_count; | |
| 2829 | + $assignment_submission = tutor_utils()->is_assignment_submitted( $row->ID ); | |
| 2830 | + $is_reviewed_by_instructor = ! count( $assignment_submission ) | |
| 2831 | + ? false | |
| 2832 | + : get_comment_meta( $assignment_submission[0]->comment_ID, 'evaluate_time', true ); | |
| 2833 | + | |
| 2834 | + if ( $assignment_submission && $is_reviewed_by_instructor ) { | |
| 2835 | + $pass_mark = tutor_utils()->get_assignment_option( $row->ID, 'pass_mark' ); | |
| 2836 | + $has_passed = false; | |
| 2837 | + foreach ( $assignment_submission as $submission ) { | |
| 2838 | + $given_mark = (int) get_comment_meta( $submission->comment_ID, 'assignment_mark', true ); | |
| 2839 | + if ( $given_mark >= $pass_mark ) { | |
| 2840 | + $has_passed = true; | |
| 2841 | + break; | |
| 2842 | + } | |
| 2952 | 2843 | } |
| 2953 | - } | |
| 2954 | - | |
| 2955 | - if ( $is_assignment_addon_enabled && tutor()->assignment_post_type === $content->post_type ) { | |
| 2956 | - if ( ! Assignments::is_assignment_passed( $content->ID, $user_id ) ) { | |
| 2957 | - ++$required_assignment_pass_count; | |
| 2844 | + if ( ! $has_passed ) { | |
| 2845 | + $required_assignment_pass++; | |
| 2958 | 2846 | } |
| 2847 | + } else { | |
| 2848 | + $required_assignment_pass++; | |
| 2959 | 2849 | } |
| 2960 | 2850 | } |
| 2961 | 2851 | |
| 2962 | - if ( ! $required_quiz_pass_count && ! $required_assignment_pass_count ) { | |
| 2963 | - return null; | |
| 2964 | - } | |
| 2852 | + $is_quiz_pass = true; | |
| 2853 | + $required_quiz_pass = 0; | |
| 2965 | 2854 | |
| 2966 | - return self::get_course_completion_requirement_message( | |
| 2967 | - $required_quiz_pass_count, | |
| 2968 | - $required_assignment_pass_count | |
| 2969 | - ); | |
| 2970 | - } | |
| 2855 | + if ( tutor_utils()->count( $quizzes ) ) { | |
| 2856 | + foreach ( $quizzes as $quiz ) { | |
| 2971 | 2857 | |
| 2972 | - /** | |
| 2973 | - * Build missing course completion requirements message. | |
| 2974 | - * | |
| 2975 | - * @since 4.0.0 | |
| 2976 | - * | |
| 2977 | - * @param int $quiz_count Total quiz count. | |
| 2978 | - * @param int $assignment_count Total assignment count. | |
| 2979 | - * | |
| 2980 | - * @return string | |
| 2981 | - */ | |
| 2982 | - private static function get_course_completion_requirement_message( $quiz_count, $assignment_count ) { | |
| 2983 | - $quiz_label = _n( 'quiz', 'quizzes', $quiz_count, 'tutor' ); | |
| 2984 | - $assignment_label = _n( 'assignment', 'assignments', $assignment_count, 'tutor' ); | |
| 2858 | + $attempt = tutor_utils()->get_quiz_attempt( $quiz->ID ); | |
| 2859 | + if ( $attempt ) { | |
| 2860 | + $passing_grade = tutor_utils()->get_quiz_option( $quiz->ID, 'passing_grade', 0 ); | |
| 2861 | + $earned_percentage = QuizModel::calculate_attempt_earned_percentage( $attempt ); | |
| 2985 | 2862 | |
| 2986 | - if ( $quiz_count && ! $assignment_count ) { | |
| 2987 | - return sprintf( | |
| 2988 | - /* translators: %1$s: item count; %2$s: item label */ | |
| 2989 | - __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), | |
| 2990 | - $quiz_count, | |
| 2991 | - $quiz_label | |
| 2992 | - ); | |
| 2863 | + if ( $earned_percentage < $passing_grade ) { | |
| 2864 | + $required_quiz_pass++; | |
| 2865 | + $is_quiz_pass = false; | |
| 2866 | + } | |
| 2867 | + } else { | |
| 2868 | + $required_quiz_pass++; | |
| 2869 | + $is_quiz_pass = false; | |
| 2870 | + } | |
| 2871 | + } | |
| 2993 | 2872 | } |
| 2994 | 2873 | |
| 2995 | - if ( ! $quiz_count && $assignment_count ) { | |
| 2996 | - return sprintf( | |
| 2997 | - /* translators: %1$s: item count; %2$s: item label */ | |
| 2998 | - __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), | |
| 2999 | - $assignment_count, | |
| 3000 | - $assignment_label | |
| 3001 | - ); | |
| 3002 | - } | |
| 2874 | + if ( ! $is_quiz_pass || $required_assignment_pass > 0 ) { | |
| 2875 | + $_msg = ''; | |
| 2876 | + $quiz_str = _n( 'quiz', 'quizzes', $required_quiz_pass, 'tutor' ); | |
| 2877 | + $assignment_str = _n( 'assignment', 'assignments', $required_assignment_pass, 'tutor' ); | |
| 3003 | 2878 | |
| 3004 | - return sprintf( | |
| 3005 | - /* translators: %1$s: quiz count; %2$s: quiz label; %3$s: assignment count; %4$s: assignment label */ | |
| 3006 | - __( 'You have to pass %1$s %2$s and %3$s %4$s to complete this course.', 'tutor' ), | |
| 3007 | - $quiz_count, | |
| 3008 | - $quiz_label, | |
| 3009 | - $assignment_count, | |
| 3010 | - $assignment_label | |
| 3011 | - ); | |
| 3012 | - } | |
| 2879 | + if ( ! $is_quiz_pass && 0 == $required_assignment_pass ) { | |
| 2880 | + /* translators: %1$s: number of quiz/assignment pass required; %2$s: quiz/assignment string */ | |
| 2881 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str ); | |
| 2882 | + } | |
| 3013 | 2883 | |
| 3014 | - /** | |
| 3015 | - * Check if all lessons and quizzes done before mark course complete. | |
| 3016 | - * | |
| 3017 | - * @since 1.5.8 | |
| 3018 | - * | |
| 3019 | - * @param string $html HTML string. | |
| 3020 | - * | |
| 3021 | - * @return string | |
| 3022 | - */ | |
| 3023 | - public function tutor_lms_hide_course_complete_btn( $html ) { | |
| 3024 | - $_msg = self::get_course_completion_restrict_msg(); | |
| 2884 | + if ( $is_quiz_pass && $required_assignment_pass > 0 ) { | |
| 2885 | + //phpcs:ignore | |
| 2886 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_assignment_pass, $assignment_str ); | |
| 2887 | + } | |
| 3025 | 2888 | |
| 3026 | - if ( $_msg ) { | |
| 2889 | + if ( ! $is_quiz_pass && $required_assignment_pass > 0 ) { | |
| 2890 | + /* translators: %1$s: number of quiz pass required; %2$s: quiz string; %3$s: number of assignment pass required; %4$s: assignment string */ | |
| 2891 | + $_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 ); | |
| 2892 | + } | |
| 2893 | + | |
| 3027 | 2894 | return '<div class="tutor-alert tutor-warning tutor-mt-28"> |
| 3028 | 2895 | <div class="tutor-alert-text"> |
| 3029 | 2896 | <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span> |
| 3030 | 2897 | <span>' . $_msg . '</span> |
| @@ -3040,9 +2907,8 @@ | ||
| 3040 | 2907 | * |
| 3041 | 2908 | * @since 1.5.8 |
| 3042 | 2909 | * |
| 3043 | 2910 | * @param string $html HTML string. |
| 3044 | - * | |
| 3045 | 2911 | * @return string |
| 3046 | 2912 | */ |
| 3047 | 2913 | public function get_generate_greadbook( $html ) { |
| 3048 | 2914 | if ( ! tutor_utils()->is_completed_course() ) { |
| @@ -3054,9 +2920,8 @@ | ||
| 3054 | 2920 | /** |
| 3055 | 2921 | * Add social share content in header |
| 3056 | 2922 | * |
| 3057 | 2923 | * @since 1.6.3 |
| 3058 | - * | |
| 3059 | 2924 | * @return void |
| 3060 | 2925 | */ |
| 3061 | 2926 | public function social_share_content() { |
| 3062 | 2927 | global $wp_query, $post; |
| @@ -3080,9 +2945,8 @@ | ||
| 3080 | 2945 | * |
| 3081 | 2946 | * @since 1.8.2 |
| 3082 | 2947 | * |
| 3083 | 2948 | * @param integer $post_id post ID. |
| 3084 | - * | |
| 3085 | 2949 | * @return void |
| 3086 | 2950 | */ |
| 3087 | 2951 | public function delete_associated_enrollment( $post_id ) { |
| 3088 | 2952 | global $wpdb; |
| @@ -3093,12 +2957,11 @@ | ||
| 3093 | 2957 | post_id |
| 3094 | 2958 | FROM |
| 3095 | 2959 | {$wpdb->postmeta} |
| 3096 | 2960 | WHERE |
| 3097 | - meta_key=%s | |
| 2961 | + meta_key='_tutor_enrolled_by_order_id' | |
| 3098 | 2962 | AND meta_value = %d |
| 3099 | 2963 | ", |
| 3100 | - EnrollmentModel::ENROLLMENT_ORDER_ID_META, | |
| 3101 | 2964 | $post_id |
| 3102 | 2965 | ) |
| 3103 | 2966 | ); |
| 3104 | 2967 | |
| @@ -3114,35 +2977,21 @@ | ||
| 3114 | 2977 | /** |
| 3115 | 2978 | * Reset course progress. |
| 3116 | 2979 | * |
| 3117 | 2980 | * @since 1.5.8 |
| 3118 | - * | |
| 3119 | 2981 | * @return void |
| 3120 | 2982 | */ |
| 3121 | 2983 | public function tutor_reset_course_progress() { |
| 3122 | 2984 | tutor_utils()->checking_nonce(); |
| 3123 | - $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); | |
| 3124 | - $context = Input::post( 'context', '' ); | |
| 3125 | - $course_reset_progress = tutor_utils()->get_option( 'course_reset_progress', false ); | |
| 3126 | - $course_retake_feature = tutor_utils()->get_option( 'course_retake_feature', false ); | |
| 2985 | + $course_id = Input::post( 'course_id' ); | |
| 3127 | 2986 | |
| 3128 | - if ( ! $course_reset_progress && 'learning-area-sidebar' === $context ) { | |
| 3129 | - $this->response_bad_request( __( 'You do not have permission to reset this course.', 'tutor' ) ); | |
| 2987 | + if ( ! $course_id || ! is_numeric( $course_id ) || ! tutor_utils()->is_enrolled( $course_id ) ) { | |
| 2988 | + wp_send_json_error( array( 'message' => __( 'Invalid Course ID or Access Denied.', 'tutor' ) ) ); | |
| 3130 | 2989 | return; |
| 3131 | 2990 | } |
| 3132 | 2991 | |
| 3133 | - if ( ! $course_retake_feature && ( 'course-landing' === $context || 'learning-area' === $context ) ) { | |
| 3134 | - $this->response_bad_request( __( 'You do not have permission to retake this course.', 'tutor' ) ); | |
| 3135 | - return; | |
| 3136 | - } | |
| 3137 | - | |
| 3138 | - if ( ! $course_id || ! is_numeric( $course_id ) || ! EnrollmentModel::is_enrolled( $course_id ) ) { | |
| 3139 | - $this->response_bad_request( __( 'Invalid Course ID or Access Denied.', 'tutor' ) ); | |
| 3140 | - return; | |
| 3141 | - } | |
| 3142 | - | |
| 3143 | 2992 | tutor_utils()->delete_course_progress( $course_id ); |
| 3144 | - $this->json_response( '', array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) ); | |
| 2993 | + wp_send_json_success( array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) ); | |
| 3145 | 2994 | } |
| 3146 | 2995 | |
| 3147 | 2996 | /** |
| 3148 | 2997 | * Do enroll if guest attempt to enroll and course is free |
| @@ -3150,9 +2999,9 @@ | ||
| 3150 | 2999 | * @since 1.9.8 |
| 3151 | 3000 | * |
| 3152 | 3001 | * @param integer $course_id course ID. |
| 3153 | 3002 | * @param integer $user_id user ID. |
| 3154 | - * | |
| 3003 | + | |
| 3155 | 3004 | * @return void |
| 3156 | 3005 | */ |
| 3157 | 3006 | public function enroll_after_login_if_attempt( int $course_id, int $user_id ) { |
| 3158 | 3007 | $course_id = sanitize_text_field( $course_id ); |
| @@ -3160,9 +3009,9 @@ | ||
| 3160 | 3009 | |
| 3161 | 3010 | if ( $course_id && $is_allowed ) { |
| 3162 | 3011 | $is_purchasable = tutor_utils()->is_course_purchasable( $course_id ); |
| 3163 | 3012 | if ( ! $is_purchasable ) { |
| 3164 | - EnrollmentModel::do_enroll( $course_id, $order_id = 0, $user_id ); | |
| 3013 | + tutor_utils()->do_enroll( $course_id, $order_id = 0, $user_id ); | |
| 3165 | 3014 | do_action( 'guest_attempt_after_enrollment', $course_id ); |
| 3166 | 3015 | } |
| 3167 | 3016 | } |
| 3168 | 3017 | } |
| @@ -3170,9 +3019,8 @@ | ||
| 3170 | 3019 | /** |
| 3171 | 3020 | * Handle course enrollment |
| 3172 | 3021 | * |
| 3173 | 3022 | * @since 2.1.0 |
| 3174 | - * | |
| 3175 | 3023 | * @return void |
| 3176 | 3024 | */ |
| 3177 | 3025 | public function course_enrollment() { |
| 3178 | 3026 | tutor_utils()->checking_nonce(); |
| @@ -3179,46 +3027,46 @@ | ||
| 3179 | 3027 | |
| 3180 | 3028 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 3181 | 3029 | $user_id = get_current_user_id(); |
| 3182 | 3030 | |
| 3183 | - if ( ! $course_id || ! $user_id ) { | |
| 3184 | - wp_send_json_error( tutor_utils()->error_message( 'invalid_req' ) ); | |
| 3185 | - } | |
| 3031 | + if ( $course_id ) { | |
| 3032 | + $password_protected = post_password_required( $course_id ); | |
| 3033 | + if ( $password_protected ) { | |
| 3034 | + wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); | |
| 3035 | + } | |
| 3186 | 3036 | |
| 3187 | - $password_protected = post_password_required( $course_id ); | |
| 3188 | - if ( $password_protected ) { | |
| 3189 | - wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); | |
| 3190 | - } | |
| 3037 | + $course = get_post( $course_id ); | |
| 3191 | 3038 | |
| 3192 | - $course = get_post( $course_id ); | |
| 3039 | + if ( 'private' === $course->post_status && ! current_user_can( 'read_private_tutor_courses' ) ) { | |
| 3040 | + wp_send_json_error( __( 'You do not have permission to enroll in this course', 'tutor' ) ); | |
| 3041 | + } | |
| 3193 | 3042 | |
| 3194 | - if ( CourseModel::STATUS_PRIVATE === $course->post_status && ! current_user_can( 'read_private_tutor_courses' ) ) { | |
| 3195 | - wp_send_json_error( __( 'You do not have permission to enroll in this course', 'tutor' ) ); | |
| 3196 | - } | |
| 3043 | + /** | |
| 3044 | + * This check was added to address a security issue where users could | |
| 3045 | + * enroll in a course via an AJAX call without purchasing it. | |
| 3046 | + * | |
| 3047 | + * To prevent this, we now verify whether the course is paid. | |
| 3048 | + * Additionally, we check if the user is already enrolled, since | |
| 3049 | + * Tutor's default behavior enrolls users automatically upon purchase. | |
| 3050 | + * | |
| 3051 | + * @since 3.9.4 | |
| 3052 | + */ | |
| 3053 | + if ( tutor_utils()->is_course_purchasable( $course_id ) ) { | |
| 3054 | + $is_enrolled = (bool) tutor_utils()->is_enrolled( $course_id, $user_id ); | |
| 3197 | 3055 | |
| 3198 | - /** | |
| 3199 | - * This check was added to address a security issue where users could | |
| 3200 | - * enroll in a course via an AJAX call without purchasing it. | |
| 3201 | - * | |
| 3202 | - * To prevent this, we now verify whether the course is paid. | |
| 3203 | - * Additionally, we check if the user is already enrolled, since | |
| 3204 | - * Tutor's default behavior enrolls users automatically upon purchase. | |
| 3205 | - * | |
| 3206 | - * @since 3.9.4 | |
| 3207 | - */ | |
| 3208 | - if ( tutor_utils()->is_course_purchasable( $course_id ) ) { | |
| 3209 | - $is_enrolled = (bool) EnrollmentModel::is_enrolled( $course_id, $user_id ); | |
| 3056 | + if ( ! $is_enrolled ) { | |
| 3057 | + wp_send_json_error( __( 'Please purchase the course before enrolling', 'tutor' ) ); | |
| 3058 | + } | |
| 3059 | + } | |
| 3210 | 3060 | |
| 3211 | - if ( ! $is_enrolled ) { | |
| 3212 | - wp_send_json_error( __( 'Please purchase the course before enrolling', 'tutor' ) ); | |
| 3061 | + $enroll = tutor_utils()->do_enroll( $course_id, 0, $user_id ); | |
| 3062 | + if ( $enroll ) { | |
| 3063 | + wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); | |
| 3064 | + } else { | |
| 3065 | + wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); | |
| 3213 | 3066 | } |
| 3214 | - } | |
| 3215 | - | |
| 3216 | - $enroll = EnrollmentModel::do_enroll( $course_id, 0, $user_id ); | |
| 3217 | - if ( $enroll ) { | |
| 3218 | - wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); | |
| 3219 | 3067 | } else { |
| 3220 | - wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); | |
| 3068 | + wp_send_json_error( __( 'Invalid course ID', 'tutor' ) ); | |
| 3221 | 3069 | } |
| 3222 | 3070 | } |
| 3223 | 3071 | |
| 3224 | 3072 | /** |
| @@ -3397,9 +3245,8 @@ | ||
| 3397 | 3245 | * @since 3.8.2 |
| 3398 | 3246 | * |
| 3399 | 3247 | * @param int $post_ID The WordPress post ID of the course. |
| 3400 | 3248 | * @param int $product_id The WooCommerce product ID to associate with the course. |
| 3401 | - * | |
| 3402 | 3249 | * @return void |
| 3403 | 3250 | */ |
| 3404 | 3251 | public static function sync_course_with_wc_product( $post_ID, $product_id ) { |
| 3405 | 3252 | |
| @@ -3424,163 +3271,6 @@ | ||
| 3424 | 3271 | |
| 3425 | 3272 | // Set course regular & sale price. |
| 3426 | 3273 | update_post_meta( $post_ID, self::COURSE_PRICE_META, $regular_price ); |
| 3427 | 3274 | update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $sale_price ); |
| 3428 | - } | |
| 3429 | - | |
| 3430 | - /** | |
| 3431 | - * Render start/resume button in enrolled course action btn. | |
| 3432 | - * | |
| 3433 | - * @since 4.0.0 | |
| 3434 | - * | |
| 3435 | - * @param int $course_id course id. | |
| 3436 | - * | |
| 3437 | - * @return void | |
| 3438 | - */ | |
| 3439 | - public function render_course_action_btn( int $course_id ) { | |
| 3440 | - $is_completed = tutor_utils()->is_completed_course( $course_id ); | |
| 3441 | - if ( $is_completed ) { | |
| 3442 | - $certificate_addon_active = tutor_utils()->is_addon_enabled( 'tutor-certificate' ); | |
| 3443 | - if ( ! $certificate_addon_active ) { | |
| 3444 | - Button::make() | |
| 3445 | - ->tag( 'a' ) | |
| 3446 | - ->label( __( 'Completed', 'tutor' ) ) | |
| 3447 | - ->icon( Icon::COMPLETED_CIRCLE ) | |
| 3448 | - ->variant( Variant::PRIMARY ) | |
| 3449 | - ->size( Size::X_SMALL ) | |
| 3450 | - ->attr( 'href', esc_url( get_the_permalink( $course_id ) ) ) | |
| 3451 | - ->render(); | |
| 3452 | - } | |
| 3453 | - return; | |
| 3454 | - } | |
| 3455 | - | |
| 3456 | - $course_progress = tutor_utils()->get_course_completed_percent( $course_id, 0, true ); | |
| 3457 | - $button_text = $course_progress['completed_percent'] > 0 ? __( 'Resume', 'tutor' ) : __( 'Start', 'tutor' ); | |
| 3458 | - $button_url = tutor_utils()->get_course_first_lesson( $course_id ); | |
| 3459 | - | |
| 3460 | - if ( ! $button_url ) { | |
| 3461 | - $button_url = get_the_permalink( $course_id ); | |
| 3462 | - } | |
| 3463 | - | |
| 3464 | - Button::make() | |
| 3465 | - ->tag( 'a' ) | |
| 3466 | - ->label( $button_text ) | |
| 3467 | - ->icon( Icon::PLAY_2 ) | |
| 3468 | - ->variant( Variant::PRIMARY ) | |
| 3469 | - ->size( Size::X_SMALL ) | |
| 3470 | - ->attr( 'href', esc_url( $button_url ) ) | |
| 3471 | - ->render(); | |
| 3472 | - } | |
| 3473 | - | |
| 3474 | - /** | |
| 3475 | - * Get the content for the course completion modal. | |
| 3476 | - * | |
| 3477 | - * This method returns HTML for displaying a modal that informs the user | |
| 3478 | - * that they have not completed all required lessons and assessments. It includes | |
| 3479 | - * the user's current progress shown as a percentage and a progress bar. | |
| 3480 | - * | |
| 3481 | - * @since 4.0.0 | |
| 3482 | - * | |
| 3483 | - * @param float $course_progress The completion percentage of the course. | |
| 3484 | - * | |
| 3485 | - * @return string The rendered HTML content for the modal. | |
| 3486 | - */ | |
| 3487 | - public static function get_complete_modal_content( float $course_progress = 0 ): string { | |
| 3488 | - ob_start(); | |
| 3489 | - ?> | |
| 3490 | - <div> | |
| 3491 | - <p class="tutor-p3 tutor-text-secondary tutor-text-center tutor-mb-7 tutor-px-11"> | |
| 3492 | - <?php esc_html_e( 'You have not completed all required lessons and assessments. ', 'tutor' ); ?> | |
| 3493 | - </p> | |
| 3494 | - <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"> | |
| 3495 | - <div class="tutor-flex tutor-items-center tutor-justify-between"> | |
| 3496 | - <div class="tutor-p3 tutor-text-secondary"> | |
| 3497 | - <?php esc_html_e( 'Your Progress', 'tutor' ); ?> | |
| 3498 | - </div> | |
| 3499 | - <div class="tutor-p1 tutor-font-bold"> | |
| 3500 | - <?php echo esc_html( number_format_i18n( $course_progress ) . '%' ); ?> | |
| 3501 | - </div> | |
| 3502 | - </div> | |
| 3503 | - <?php | |
| 3504 | - Progress::make()->type( 'bar' )->value( $course_progress )->render(); | |
| 3505 | - ?> | |
| 3506 | - </div> | |
| 3507 | - </div> | |
| 3508 | - <?php | |
| 3509 | - return ob_get_clean(); | |
| 3510 | - } | |
| 3511 | - | |
| 3512 | - /** | |
| 3513 | - * Render the course complete button. | |
| 3514 | - * | |
| 3515 | - * Displays a button that allows the user to complete the course. If the course | |
| 3516 | - * progress is less than 100%, clicking the button shows a modal informing the user | |
| 3517 | - * that not all requirements are fulfilled. If the course progress is 100%, | |
| 3518 | - * clicking the button attempts to complete the course. | |
| 3519 | - * | |
| 3520 | - * @since 4.0.0 | |
| 3521 | - * | |
| 3522 | - * @param string $modal_id The HTML ID of the modal to display if not complete. | |
| 3523 | - * @param int $course_id The ID of the course. | |
| 3524 | - * @param float $course_progress The current completion percentage of the course. | |
| 3525 | - * @param string $size The button size. | |
| 3526 | - * @param string $tooltip Optional. Tooltip message. | |
| 3527 | - * @param bool $block Optional. Whether the button is full-width. | |
| 3528 | - * | |
| 3529 | - * @return void | |
| 3530 | - */ | |
| 3531 | - 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 { | |
| 3532 | - $button = Button::make() | |
| 3533 | - ->variant( Variant::SECONDARY ) | |
| 3534 | - ->label( __( 'Complete the Course', 'tutor' ) ) | |
| 3535 | - ->icon( Icon::TICK_MARK ) | |
| 3536 | - ->size( $size ) | |
| 3537 | - ->block( $block ) | |
| 3538 | - ->attr( 'type', 'button' ); | |
| 3539 | - | |
| 3540 | - if ( ! empty( $tooltip ) ) { | |
| 3541 | - $button->disabled(); | |
| 3542 | - } | |
| 3543 | - | |
| 3544 | - if ( $course_progress < 100 ) { | |
| 3545 | - $button->attr( '@click', "TutorCore.modal.showModal('{$modal_id}')" ); | |
| 3546 | - } else { | |
| 3547 | - $button->attr( '@click', "handleCourseComplete({$course_id})" ); | |
| 3548 | - $button->attr( ':class', "courseCompleteMutation?.isPending ? 'tutor-btn-loading' : ''" ); | |
| 3549 | - $button->attr( ':disabled', 'courseCompleteMutation?.isPending' ); | |
| 3550 | - } | |
| 3551 | - | |
| 3552 | - if ( ! empty( $tooltip ) ) { | |
| 3553 | - Tooltip::make() | |
| 3554 | - ->content( $tooltip ) | |
| 3555 | - ->placement( Tooltip::PLACEMENT_BOTTOM ) | |
| 3556 | - ->arrow( Tooltip::ARROW_CENTER ) | |
| 3557 | - ->trigger_element( $button->get() ) | |
| 3558 | - ->render(); | |
| 3559 | - } else { | |
| 3560 | - $button->render(); | |
| 3561 | - } | |
| 3562 | - } | |
| 3563 | - | |
| 3564 | - /** | |
| 3565 | - * Render course retake button | |
| 3566 | - * | |
| 3567 | - * @since 4.0.0 | |
| 3568 | - * | |
| 3569 | - * @param string $modal_id Modal id. | |
| 3570 | - * @param string $size The button size. | |
| 3571 | - * @param bool $block Optional. Whether the button is full-width. | |
| 3572 | - * | |
| 3573 | - * @return void | |
| 3574 | - */ | |
| 3575 | - public static function render_course_retake_btn( string $modal_id, string $size = Size::MEDIUM, bool $block = false ): void { | |
| 3576 | - Button::make() | |
| 3577 | - ->variant( Variant::SECONDARY ) | |
| 3578 | - ->label( __( 'Retake this Course', 'tutor' ) ) | |
| 3579 | - ->icon( Icon::RELOAD_4 ) | |
| 3580 | - ->size( $size ) | |
| 3581 | - ->block( $block ) | |
| 3582 | - ->attr( 'type', 'button' ) | |
| 3583 | - ->attr( '@click', "TutorCore.modal.showModal('{$modal_id}')" ) | |
| 3584 | - ->render(); | |
| 3585 | 3275 | } |
| 3586 | 3276 | } |