| @@ -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; | |
| 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; | |
| 22 | +use Tutor\Ecommerce\Tax; | |
| 26 | 23 | use Tutor\Traits\JsonResponse; |
| 27 | 24 | use Tutor\Helpers\ValidationHelper; |
| 28 | -use Tutor\Models\EnrollmentModel; | |
| 29 | -use Tutor\Options_V2; | |
| 30 | -use TUTOR_ASSIGNMENTS\Assignments; | |
| 25 | +use Tutor\Models\QuizModel; | |
| 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 | */ |
| @@ -252,9 +248,9 @@ | ||
| 252 | 248 | * @since 1.9.8 |
| 253 | 249 | */ |
| 254 | 250 | add_action( 'tutor_do_enroll_after_login_if_attempt', array( $this, 'enroll_after_login_if_attempt' ), 10, 2 ); |
| 255 | 251 | |
| 256 | - add_action( 'wp_ajax_tutor_update_course_content_order', array( $this, 'ajax_update_course_content_order' ) ); | |
| 252 | + add_action( 'wp_ajax_tutor_update_course_content_order', array( $this, 'tutor_update_course_content_order' ) ); | |
| 257 | 253 | |
| 258 | 254 | add_action( 'wp_ajax_tutor_get_wc_product', array( $this, 'get_wc_product' ) ); |
| 259 | 255 | add_action( 'wp_ajax_tutor_get_wc_products', array( $this, 'get_wc_products' ) ); |
| 260 | 256 | |
| @@ -302,13 +298,9 @@ | ||
| 302 | 298 | add_filter( 'tutor_user_list_args', array( $this, 'user_list_args_for_instructor' ) ); |
| 303 | 299 | |
| 304 | 300 | add_filter( 'template_include', array( $this, 'handle_password_protected' ) ); |
| 305 | 301 | 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 | 302 | 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 | 303 | } |
| 312 | 304 | |
| 313 | 305 | /** |
| 314 | 306 | * Handle schedule courses preview for instructors. |
| @@ -350,31 +342,8 @@ | ||
| 350 | 342 | } |
| 351 | 343 | } |
| 352 | 344 | |
| 353 | 345 | /** |
| 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 | 346 | * Handle password protected course/bundle. |
| 378 | 347 | * |
| 379 | 348 | * @since 3.0.0 |
| 380 | 349 | * |
| @@ -912,9 +881,9 @@ | ||
| 912 | 881 | |
| 913 | 882 | update_post_meta( $course_id, self::COURSE_PRICE_TYPE_META, self::PRICE_TYPE_FREE ); |
| 914 | 883 | |
| 915 | 884 | $link = admin_url( 'admin.php?page=create-course' ); |
| 916 | - if ( tutor()->has_pro && Input::post( 'from_dashboard', false, Input::TYPE_BOOL ) ) { | |
| 885 | + if ( Input::post( 'from_dashboard', false, Input::TYPE_BOOL ) ) { | |
| 917 | 886 | $link = tutor_utils()->tutor_dashboard_url( 'create-course' ); |
| 918 | 887 | } |
| 919 | 888 | |
| 920 | 889 | $link = add_query_arg( array( 'course_id' => $course_id ), $link ); |
| @@ -932,14 +901,15 @@ | ||
| 932 | 901 | * Get course list |
| 933 | 902 | * |
| 934 | 903 | * @since 3.0.0 |
| 935 | 904 | * |
| 936 | - * @since 3.2.0 Refactor the arguments & response as per new design. | |
| 905 | + * @since 3.2.0 | |
| 937 | 906 | * |
| 907 | + * Refactor the arguments & response as per new design | |
| 908 | + * | |
| 938 | 909 | * @return void |
| 939 | 910 | */ |
| 940 | 911 | public function ajax_course_list() { |
| 941 | - tutor_utils()->check_nonce(); | |
| 942 | 912 | $this->check_access(); |
| 943 | 913 | |
| 944 | 914 | $limit = Input::post( 'limit', 10, Input::TYPE_INT ); |
| 945 | 915 | $offset = Input::post( 'offset', 0, Input::TYPE_INT ); |
| @@ -959,9 +929,14 @@ | ||
| 959 | 929 | ); |
| 960 | 930 | |
| 961 | 931 | $exclude = Input::post( 'exclude', array(), Input::TYPE_ARRAY ); |
| 962 | 932 | if ( count( $exclude ) ) { |
| 963 | - $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 | + ); | |
| 964 | 939 | $args['post__not_in'] = $exclude; |
| 965 | 940 | } |
| 966 | 941 | |
| 967 | 942 | $courses = CourseModel::get_courses_by_args( $args ); |
| @@ -1101,12 +1076,8 @@ | ||
| 1101 | 1076 | ) |
| 1102 | 1077 | ); |
| 1103 | 1078 | |
| 1104 | 1079 | $course_id = (int) $params['course_id']; |
| 1105 | - if ( ! $course_id ) { | |
| 1106 | - $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); | |
| 1107 | - } | |
| 1108 | - | |
| 1109 | 1080 | $this->check_access( $course_id ); |
| 1110 | 1081 | |
| 1111 | 1082 | $errors = array(); |
| 1112 | 1083 | $validation = $this->validate_inputs( $params ); |
| @@ -1195,32 +1166,15 @@ | ||
| 1195 | 1166 | public function ajax_unlink_page_builder() { |
| 1196 | 1167 | tutor_utils()->check_nonce(); |
| 1197 | 1168 | |
| 1198 | 1169 | $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 | - | |
| 1170 | + $builder = Input::post( 'builder' ); | |
| 1203 | 1171 | $this->check_access( $course_id ); |
| 1204 | 1172 | |
| 1205 | - $builder = Input::post( 'builder' ); | |
| 1206 | 1173 | if ( 'elementor' === $builder ) { |
| 1207 | 1174 | delete_post_meta( $course_id, '_elementor_edit_mode' ); |
| 1208 | 1175 | } elseif ( 'droip' === $builder ) { |
| 1209 | 1176 | 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 | 1177 | } |
| 1224 | 1178 | |
| 1225 | 1179 | $this->json_response( |
| 1226 | 1180 | __( 'Builder unlinked successfully.', 'tutor' ), |
| @@ -1276,23 +1230,20 @@ | ||
| 1276 | 1230 | /** |
| 1277 | 1231 | * Get course contents |
| 1278 | 1232 | * |
| 1279 | 1233 | * @since 3.0.0 |
| 1280 | - * | |
| 1281 | - * @return void | |
| 1282 | 1234 | */ |
| 1283 | 1235 | public function ajax_course_contents() { |
| 1284 | 1236 | tutor_utils()->check_nonce(); |
| 1285 | 1237 | |
| 1286 | - $errors = array(); | |
| 1287 | 1238 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1288 | 1239 | |
| 1289 | - if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1240 | + $this->check_access( $course_id ); | |
| 1241 | + | |
| 1242 | + if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1290 | 1243 | $errors['course_id'] = __( 'Invalid course id', 'tutor' ); |
| 1291 | 1244 | } |
| 1292 | 1245 | |
| 1293 | - $this->check_access( $course_id ); | |
| 1294 | - | |
| 1295 | 1246 | if ( ! empty( $errors ) ) { |
| 1296 | 1247 | $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); |
| 1297 | 1248 | } |
| 1298 | 1249 | |
| @@ -1316,14 +1267,14 @@ | ||
| 1316 | 1267 | |
| 1317 | 1268 | $errors = array(); |
| 1318 | 1269 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 1319 | 1270 | |
| 1320 | - if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1271 | + $this->check_access( $course_id ); | |
| 1272 | + | |
| 1273 | + if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { | |
| 1321 | 1274 | $errors['course_id'] = __( 'Invalid course id', 'tutor' ); |
| 1322 | 1275 | } |
| 1323 | 1276 | |
| 1324 | - $this->check_access( $course_id ); | |
| 1325 | - | |
| 1326 | 1277 | if ( ! empty( $errors ) ) { |
| 1327 | 1278 | $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); |
| 1328 | 1279 | } |
| 1329 | 1280 | |
| @@ -1334,9 +1285,9 @@ | ||
| 1334 | 1285 | $price = 0; |
| 1335 | 1286 | $sale_price = 0; |
| 1336 | 1287 | $product_id = tutor_utils()->get_course_product_id( $course_id ); |
| 1337 | 1288 | |
| 1338 | - if ( WooCommerce::MONETIZE_BY === $monetize_by ) { | |
| 1289 | + if ( 'wc' === $monetize_by ) { | |
| 1339 | 1290 | $product = wc_get_product( $product_id ); |
| 1340 | 1291 | if ( $product ) { |
| 1341 | 1292 | $product_name = $product->get_name(); |
| 1342 | 1293 | $price = $product->get_regular_price(); |
| @@ -1343,9 +1294,9 @@ | ||
| 1343 | 1294 | $sale_price = $product->get_sale_price(); |
| 1344 | 1295 | } |
| 1345 | 1296 | } |
| 1346 | 1297 | |
| 1347 | - if ( Ecommerce::MONETIZE_BY === $monetize_by ) { | |
| 1298 | + if ( 'tutor' === $monetize_by ) { | |
| 1348 | 1299 | $price = get_post_meta( $course_id, self::COURSE_PRICE_META, true ); |
| 1349 | 1300 | $sale_price = get_post_meta( $course_id, self::COURSE_SALE_PRICE_META, true ); |
| 1350 | 1301 | } |
| 1351 | 1302 | |
| @@ -1527,9 +1478,8 @@ | ||
| 1527 | 1478 | /** |
| 1528 | 1479 | * Localized only options to protect sensitive info like API keys. |
| 1529 | 1480 | */ |
| 1530 | 1481 | $required_options = array( |
| 1531 | - 'learning_mode', | |
| 1532 | 1482 | 'monetize_by', |
| 1533 | 1483 | 'enable_course_marketplace', |
| 1534 | 1484 | 'course_permalink_base', |
| 1535 | 1485 | 'supported_video_sources', |
| @@ -1731,38 +1681,23 @@ | ||
| 1731 | 1681 | /** |
| 1732 | 1682 | * Update course content order |
| 1733 | 1683 | * |
| 1734 | 1684 | * @since 1.0.0 |
| 1735 | - * @since 3.9.9 Check if user can manage course before updating order. | |
| 1736 | - * | |
| 1737 | 1685 | * @return void |
| 1738 | 1686 | */ |
| 1739 | - public function ajax_update_course_content_order() { | |
| 1687 | + public function tutor_update_course_content_order() { | |
| 1740 | 1688 | tutor_utils()->checking_nonce(); |
| 1741 | 1689 | |
| 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 | 1690 | if ( Input::has( 'content_parent' ) ) { |
| 1761 | 1691 | $content_parent = Input::post( 'content_parent', array(), Input::TYPE_ARRAY ); |
| 1762 | 1692 | $topic_id = tutor_utils()->array_get( 'parent_topic_id', $content_parent ); |
| 1763 | 1693 | $content_id = tutor_utils()->array_get( 'content_id', $content_parent ); |
| 1764 | 1694 | |
| 1695 | + if ( ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) { | |
| 1696 | + wp_send_json_success( array( 'message' => __( 'Access Denied!', 'tutor' ) ) ); | |
| 1697 | + exit; | |
| 1698 | + } | |
| 1699 | + | |
| 1765 | 1700 | // Update the parent topic id of the content. |
| 1766 | 1701 | global $wpdb; |
| 1767 | 1702 | $wpdb->update( $wpdb->posts, array( 'post_parent' => $topic_id ), array( 'ID' => $content_id ) ); |
| 1768 | 1703 | } |
| @@ -1767,9 +1702,9 @@ | ||
| 1767 | 1702 | $wpdb->update( $wpdb->posts, array( 'post_parent' => $topic_id ), array( 'ID' => $content_id ) ); |
| 1768 | 1703 | } |
| 1769 | 1704 | |
| 1770 | 1705 | // Save course content order. |
| 1771 | - $this->save_course_content_order( $sorting_order ); | |
| 1706 | + $this->save_course_content_order(); | |
| 1772 | 1707 | |
| 1773 | 1708 | wp_send_json_success(); |
| 1774 | 1709 | } |
| 1775 | 1710 | |
| @@ -1796,11 +1731,9 @@ | ||
| 1796 | 1731 | /** |
| 1797 | 1732 | * Restrict media |
| 1798 | 1733 | * |
| 1799 | 1734 | * @since 1.0.0 |
| 1800 | - * | |
| 1801 | 1735 | * @param string $where where clause. |
| 1802 | - * | |
| 1803 | 1736 | * @return string |
| 1804 | 1737 | */ |
| 1805 | 1738 | public function restrict_media( $where ) { |
| 1806 | 1739 | $action = Input::post( 'action' ); |
| @@ -1816,58 +1749,58 @@ | ||
| 1816 | 1749 | /** |
| 1817 | 1750 | * Save course content order |
| 1818 | 1751 | * |
| 1819 | 1752 | * @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 | 1753 | * @return void |
| 1825 | 1754 | */ |
| 1826 | - private function save_course_content_order( $sort_order = array() ) { | |
| 1755 | + private function save_course_content_order() { | |
| 1827 | 1756 | global $wpdb; |
| 1828 | 1757 | |
| 1829 | - if ( ! tutor_utils()->count( $sort_order ) ) { | |
| 1830 | - return; | |
| 1831 | - } | |
| 1758 | + $new_order = Input::post( 'tutor_topics_lessons_sorting' ); | |
| 1759 | + if ( ! empty( $new_order ) ) { | |
| 1760 | + $order = json_decode( $new_order, true ); | |
| 1832 | 1761 | |
| 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 | - ); | |
| 1762 | + if ( is_array( $order ) && count( $order ) ) { | |
| 1763 | + $i = 0; | |
| 1764 | + foreach ( $order as $topic ) { | |
| 1765 | + $i++; | |
| 1766 | + $wpdb->update( | |
| 1767 | + $wpdb->posts, | |
| 1768 | + array( 'menu_order' => $i ), | |
| 1769 | + array( 'ID' => $topic['topic_id'] ) | |
| 1770 | + ); | |
| 1841 | 1771 | |
| 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 | - ); | |
| 1772 | + /** | |
| 1773 | + * Removing All lesson with topic | |
| 1774 | + */ | |
| 1850 | 1775 | |
| 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 | 1776 | $wpdb->update( |
| 1863 | 1777 | $wpdb->posts, |
| 1864 | - array( | |
| 1865 | - 'post_parent' => $topic['topic_id'], | |
| 1866 | - 'menu_order' => $lesson_key, | |
| 1867 | - ), | |
| 1868 | - array( 'ID' => $lesson_id ) | |
| 1778 | + array( 'post_parent' => 0 ), | |
| 1779 | + array( 'post_parent' => $topic['topic_id'] ) | |
| 1869 | 1780 | ); |
| 1781 | + | |
| 1782 | + /** | |
| 1783 | + * Lesson Attaching with topic ID | |
| 1784 | + * Sorting lesson | |
| 1785 | + */ | |
| 1786 | + if ( isset( $topic['lesson_ids'] ) ) { | |
| 1787 | + $lesson_ids = $topic['lesson_ids']; | |
| 1788 | + } else { | |
| 1789 | + $lesson_ids = array(); | |
| 1790 | + } | |
| 1791 | + if ( count( $lesson_ids ) ) { | |
| 1792 | + foreach ( $lesson_ids as $lesson_key => $lesson_id ) { | |
| 1793 | + $wpdb->update( | |
| 1794 | + $wpdb->posts, | |
| 1795 | + array( | |
| 1796 | + 'post_parent' => $topic['topic_id'], | |
| 1797 | + 'menu_order' => $lesson_key, | |
| 1798 | + ), | |
| 1799 | + array( 'ID' => $lesson_id ) | |
| 1800 | + ); | |
| 1801 | + } | |
| 1802 | + } | |
| 1870 | 1803 | } |
| 1871 | 1804 | } |
| 1872 | 1805 | } |
| 1873 | 1806 | } |
| @@ -1938,14 +1871,12 @@ | ||
| 1938 | 1871 | } |
| 1939 | 1872 | //phpcs:enable WordPress.Security.NonceVerification.Missing |
| 1940 | 1873 | } |
| 1941 | 1874 | |
| 1942 | - $sorting_order = Input::post( 'tutor_topics_lessons_sorting', '' ); | |
| 1943 | - $sorting_order = json_decode( $sorting_order, true ) ?? array(); | |
| 1944 | 1875 | /** |
| 1945 | 1876 | * Sorting Topics and lesson |
| 1946 | 1877 | */ |
| 1947 | - $this->save_course_content_order( $sorting_order ); | |
| 1878 | + $this->save_course_content_order(); | |
| 1948 | 1879 | |
| 1949 | 1880 | // Additional data like course intro video. |
| 1950 | 1881 | if ( $additional_data_edit ) { |
| 1951 | 1882 | // Sanitize data through helper method. |
| @@ -2140,14 +2071,8 @@ | ||
| 2140 | 2071 | */ |
| 2141 | 2072 | |
| 2142 | 2073 | $is_purchasable = tutor_utils()->is_course_purchasable( $course_id ); |
| 2143 | 2074 | |
| 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 | 2075 | /** |
| 2151 | 2076 | * If is is not purchasable, it's free, and enroll right now |
| 2152 | 2077 | * If purchasable, then process purchase. |
| 2153 | 2078 | * |
| @@ -2157,9 +2082,9 @@ | ||
| 2157 | 2082 | // Process purchase. |
| 2158 | 2083 | |
| 2159 | 2084 | } else { |
| 2160 | 2085 | // Free enroll. |
| 2161 | - EnrollmentModel::do_enroll( $course_id ); | |
| 2086 | + tutor_utils()->do_enroll( $course_id ); | |
| 2162 | 2087 | } |
| 2163 | 2088 | |
| 2164 | 2089 | $referer_url = wp_get_referer(); |
| 2165 | 2090 | wp_safe_redirect( tutor_utils()->get_nocache_url( $referer_url ) ); |
| @@ -2193,23 +2118,16 @@ | ||
| 2193 | 2118 | if ( ! $user_id ) { |
| 2194 | 2119 | die( esc_html__( 'Please Sign-In', 'tutor' ) ); |
| 2195 | 2120 | } |
| 2196 | 2121 | |
| 2197 | - if ( ! EnrollmentModel::is_enrolled( $course_id, $user_id ) ) { | |
| 2198 | - die( esc_html__( 'User is not enrolled in course', 'tutor' ) ); | |
| 2199 | - } | |
| 2200 | - | |
| 2201 | 2122 | /** |
| 2202 | 2123 | * Filter hook provided to restrict course completion. This is useful |
| 2203 | 2124 | * for specific cases like prerequisites. WP_Error should be returned |
| 2204 | 2125 | * from the filter value to prevent the completion. |
| 2205 | 2126 | */ |
| 2206 | - $can_complete = apply_filters( 'tutor_user_can_complete_course', CourseModel::can_complete_course( $course_id, $user_id ), $user_id, $course_id ); | |
| 2207 | - | |
| 2127 | + $can_complete = apply_filters( 'tutor_user_can_complete_course', true, $user_id, $course_id ); | |
| 2208 | 2128 | if ( is_wp_error( $can_complete ) ) { |
| 2209 | 2129 | 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 | 2130 | } else { |
| 2213 | 2131 | CourseModel::mark_course_as_completed( $course_id, $user_id ); |
| 2214 | 2132 | // Set temporary identifier to show review pop up. |
| 2215 | 2133 | self::set_review_popup_data( $user_id, $course_id, $permalink ); |
| @@ -2219,52 +2137,8 @@ | ||
| 2219 | 2137 | } |
| 2220 | 2138 | } |
| 2221 | 2139 | |
| 2222 | 2140 | /** |
| 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 | 2141 | * Set data for review popup. |
| 2268 | 2142 | * |
| 2269 | 2143 | * @since 2.2.5 |
| 2270 | 2144 | * @since 2.4.0 removed $permalink param. store user meta instead of option data. |
| @@ -2290,34 +2164,18 @@ | ||
| 2290 | 2164 | * @since 1.0.0 |
| 2291 | 2165 | * @return void |
| 2292 | 2166 | */ |
| 2293 | 2167 | public function popup_review_form() { |
| 2294 | - if ( ! is_user_logged_in() ) { | |
| 2295 | - return; | |
| 2296 | - } | |
| 2168 | + if ( is_user_logged_in() ) { | |
| 2169 | + $user_id = get_current_user_id(); | |
| 2170 | + $course_id = get_the_ID(); | |
| 2171 | + $meta_key = User::get_review_popup_meta( $course_id ); | |
| 2172 | + $review_course_id = (int) get_user_meta( $user_id, $meta_key, true ); | |
| 2297 | 2173 | |
| 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; | |
| 2174 | + if ( is_single() && $course_id === $review_course_id ) { | |
| 2175 | + include tutor()->path . 'views/modal/review.php'; | |
| 2176 | + } | |
| 2302 | 2177 | } |
| 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 | 2178 | } |
| 2321 | 2179 | |
| 2322 | 2180 | /** |
| 2323 | 2181 | * Review popup data clear |
| @@ -2345,9 +2203,8 @@ | ||
| 2345 | 2203 | /** |
| 2346 | 2204 | * Delete course delete from frontend dashboard |
| 2347 | 2205 | * |
| 2348 | 2206 | * @since 2.0.0 |
| 2349 | - * | |
| 2350 | 2207 | * @return void |
| 2351 | 2208 | */ |
| 2352 | 2209 | public function tutor_delete_dashboard_course() { |
| 2353 | 2210 | tutor_utils()->checking_nonce(); |
| @@ -2366,9 +2223,9 @@ | ||
| 2366 | 2223 | wp_send_json_error( array( 'message' => __( 'Only main instructor can delete this course', 'tutor' ) ) ); |
| 2367 | 2224 | } |
| 2368 | 2225 | |
| 2369 | 2226 | // Check if user is only an instructor. |
| 2370 | - if ( ! User::is_admin() ) { | |
| 2227 | + if ( ! current_user_can( 'administrator' ) ) { | |
| 2371 | 2228 | // Check if instructor can trash course. |
| 2372 | 2229 | $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ); |
| 2373 | 2230 | |
| 2374 | 2231 | if ( ! $can_trash_post ) { |
| @@ -2378,9 +2235,9 @@ | ||
| 2378 | 2235 | |
| 2379 | 2236 | $trash_course = wp_update_post( |
| 2380 | 2237 | array( |
| 2381 | 2238 | 'ID' => $course_id, |
| 2382 | - 'post_status' => CourseModel::STATUS_TRASH, | |
| 2239 | + 'post_status' => 'trash', | |
| 2383 | 2240 | ) |
| 2384 | 2241 | ); |
| 2385 | 2242 | |
| 2386 | 2243 | if ( $trash_course ) { |
| @@ -2495,19 +2352,23 @@ | ||
| 2495 | 2352 | update_post_meta( $product_id, '_subscription_price', $course_price ); |
| 2496 | 2353 | } |
| 2497 | 2354 | |
| 2498 | 2355 | // 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() ); | |
| 2356 | + update_post_meta( $post_ID, self::COURSE_PRICE_META, $product_obj->get_regular_price() ); | |
| 2357 | + update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $product_obj->get_sale_price() ); | |
| 2500 | 2358 | } else { |
| 2501 | 2359 | // Create new WC product name with course title. |
| 2502 | 2360 | $product_id = self::create_wc_product( $course->post_title, $course_price, $sale_price ); |
| 2503 | 2361 | if ( $product_id ) { |
| 2504 | 2362 | $product_obj = wc_get_product( $product_id ); |
| 2363 | + update_post_meta( $post_ID, self::COURSE_PRODUCT_ID_META, $product_id ); | |
| 2364 | + // Mark product for woocommerce. | |
| 2365 | + update_post_meta( $product_id, '_virtual', 'yes' ); | |
| 2366 | + update_post_meta( $product_id, '_tutor_product', 'yes' ); | |
| 2505 | 2367 | |
| 2506 | - self::sync_course_with_wc_product( $post_ID, $product_id ); | |
| 2507 | - | |
| 2508 | 2368 | // 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() ); | |
| 2369 | + update_post_meta( $post_ID, self::COURSE_PRICE_META, $product_obj->get_regular_price() ); | |
| 2370 | + update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $product_obj->get_sale_price() ); | |
| 2510 | 2371 | } |
| 2511 | 2372 | } |
| 2512 | 2373 | |
| 2513 | 2374 | $course_post_thumbnail = Input::post( 'thumbnail_id', 0, Input::TYPE_INT ); |
| @@ -2555,8 +2416,33 @@ | ||
| 2555 | 2416 | } |
| 2556 | 2417 | } |
| 2557 | 2418 | |
| 2558 | 2419 | /** |
| 2420 | + * Add Course level to course settings | |
| 2421 | + * | |
| 2422 | + * @since 1.4.1 | |
| 2423 | + * | |
| 2424 | + * @param array $args arguments. | |
| 2425 | + * @return array | |
| 2426 | + */ | |
| 2427 | + public function add_course_level_to_settings( $args ) { | |
| 2428 | + $course_id = get_the_ID(); | |
| 2429 | + $levels = tutor_utils()->course_levels(); | |
| 2430 | + $course_level = get_post_meta( $course_id, '_tutor_course_level', true ); | |
| 2431 | + | |
| 2432 | + $args['general']['fields']['_tutor_course_level'] = array( | |
| 2433 | + 'type' => 'select', | |
| 2434 | + 'label' => __( 'Difficulty Level', 'tutor' ), | |
| 2435 | + 'label_title' => __( 'Enable', 'tutor' ), | |
| 2436 | + 'options' => $levels, | |
| 2437 | + 'value' => $course_level ? $course_level : 'intermediate', | |
| 2438 | + 'desc' => __( 'Course difficulty level', 'tutor' ), | |
| 2439 | + ); | |
| 2440 | + | |
| 2441 | + return $args; | |
| 2442 | + } | |
| 2443 | + | |
| 2444 | + /** | |
| 2559 | 2445 | * Check if course starting |
| 2560 | 2446 | * |
| 2561 | 2447 | * @since 1.4.8 |
| 2562 | 2448 | * @return void |
| @@ -2576,9 +2462,8 @@ | ||
| 2576 | 2462 | /** |
| 2577 | 2463 | * Add Course level to course settings |
| 2578 | 2464 | * |
| 2579 | 2465 | * @since 1.4.8 |
| 2580 | - * | |
| 2581 | 2466 | * @return void |
| 2582 | 2467 | */ |
| 2583 | 2468 | public function course_elements_enable_disable() { |
| 2584 | 2469 | add_filter( 'tutor_course/single/completing-progress-bar', array( $this, 'enable_disable_course_progress_bar' ) ); |
| @@ -2611,9 +2496,8 @@ | ||
| 2611 | 2496 | * |
| 2612 | 2497 | * @since 1.4.8 |
| 2613 | 2498 | * |
| 2614 | 2499 | * @param string $html HTML string. |
| 2615 | - * | |
| 2616 | 2500 | * @return string |
| 2617 | 2501 | */ |
| 2618 | 2502 | public function enable_disable_material_includes( $html ) { |
| 2619 | 2503 | $disable_option = ! (bool) get_tutor_option( 'enable_course_material', true, true ); |
| @@ -2628,9 +2512,8 @@ | ||
| 2628 | 2512 | * |
| 2629 | 2513 | * @since 1.4.8 |
| 2630 | 2514 | * |
| 2631 | 2515 | * @param string $html HTML string. |
| 2632 | - * | |
| 2633 | 2516 | * @return string |
| 2634 | 2517 | */ |
| 2635 | 2518 | public function enable_disable_course_content( $html ) { |
| 2636 | 2519 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_description', true, true ); |
| @@ -2645,9 +2528,8 @@ | ||
| 2645 | 2528 | * |
| 2646 | 2529 | * @since 1.4.8 |
| 2647 | 2530 | * |
| 2648 | 2531 | * @param string $html HTML string. |
| 2649 | - * | |
| 2650 | 2532 | * @return string |
| 2651 | 2533 | */ |
| 2652 | 2534 | public function enable_disable_course_benefits( $html ) { |
| 2653 | 2535 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_benefits', true, true ); |
| @@ -2662,9 +2544,8 @@ | ||
| 2662 | 2544 | * |
| 2663 | 2545 | * @since 1.4.8 |
| 2664 | 2546 | * |
| 2665 | 2547 | * @param string $html HTML string. |
| 2666 | - * | |
| 2667 | 2548 | * @return string |
| 2668 | 2549 | */ |
| 2669 | 2550 | public function enable_disable_course_requirements( $html ) { |
| 2670 | 2551 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_requirements', true, true ); |
| @@ -2679,9 +2560,8 @@ | ||
| 2679 | 2560 | * |
| 2680 | 2561 | * @since 1.4.8 |
| 2681 | 2562 | * |
| 2682 | 2563 | * @param string $html HTML string. |
| 2683 | - * | |
| 2684 | 2564 | * @return string |
| 2685 | 2565 | */ |
| 2686 | 2566 | public function enable_disable_course_target_audience( $html ) { |
| 2687 | 2567 | $disable_option = ! (bool) tutor_utils()->get_option( 'enable_course_target_audience', true, true ); |
| @@ -2726,9 +2606,9 @@ | ||
| 2726 | 2606 | unset( $items['reviews'] ); |
| 2727 | 2607 | } |
| 2728 | 2608 | |
| 2729 | 2609 | // Whether enrollment require. |
| 2730 | - $is_enrolled = EnrollmentModel::is_enrolled(); | |
| 2610 | + $is_enrolled = tutor_utils()->is_enrolled(); | |
| 2731 | 2611 | |
| 2732 | 2612 | return array_filter( |
| 2733 | 2613 | $items, |
| 2734 | 2614 | function ( $item ) use ( $is_enrolled ) { |
| @@ -2743,9 +2623,8 @@ | ||
| 2743 | 2623 | /** |
| 2744 | 2624 | * Filter product in shop page |
| 2745 | 2625 | * |
| 2746 | 2626 | * @since 1.4.9 |
| 2747 | - * | |
| 2748 | 2627 | * @return void|null |
| 2749 | 2628 | */ |
| 2750 | 2629 | public function filter_product_in_shop_page() { |
| 2751 | 2630 | $hide_course_from_shop_page = (bool) get_tutor_option( 'hide_course_from_shop_page' ); |
| @@ -2761,9 +2640,8 @@ | ||
| 2761 | 2640 | /** |
| 2762 | 2641 | * Tutor product meta query |
| 2763 | 2642 | * |
| 2764 | 2643 | * @since 1.4.9 |
| 2765 | - * | |
| 2766 | 2644 | * @return array |
| 2767 | 2645 | */ |
| 2768 | 2646 | public function tutor_product_meta_query() { |
| 2769 | 2647 | $meta_query = array( |
| @@ -2778,9 +2656,8 @@ | ||
| 2778 | 2656 | * |
| 2779 | 2657 | * @since 1.4.9 |
| 2780 | 2658 | * |
| 2781 | 2659 | * @param \WP_Query $wp_query WP Query instance. |
| 2782 | - * | |
| 2783 | 2660 | * @return \WP_Query |
| 2784 | 2661 | */ |
| 2785 | 2662 | public function filter_woocommerce_product_query( $wp_query ) { |
| 2786 | 2663 | $product_ids = $this->get_connected_wc_product_ids(); |
| @@ -2822,9 +2699,8 @@ | ||
| 2822 | 2699 | * |
| 2823 | 2700 | * @since 1.4.9 |
| 2824 | 2701 | * |
| 2825 | 2702 | * @param \WP_Query $query WP Query instance. |
| 2826 | - * | |
| 2827 | 2703 | * @return \WP_Query |
| 2828 | 2704 | */ |
| 2829 | 2705 | public function filter_edd_downloads_query( $query ) { |
| 2830 | 2706 | $query['meta_query'][] = $this->tutor_product_meta_query(); |
| @@ -2836,9 +2712,8 @@ | ||
| 2836 | 2712 | * |
| 2837 | 2713 | * @since 1.4.9 |
| 2838 | 2714 | * |
| 2839 | 2715 | * @param \WP_Query $wp_query WP Query instance. |
| 2840 | - * | |
| 2841 | 2716 | * @return \WP_Query |
| 2842 | 2717 | */ |
| 2843 | 2718 | public function filter_archive_meta_query( $wp_query ) { |
| 2844 | 2719 | if ( ! is_admin() && $wp_query->is_archive && $wp_query->get( 'post_type' ) === 'download' ) { |
| @@ -2852,9 +2727,8 @@ | ||
| 2852 | 2727 | * |
| 2853 | 2728 | * @since 1.5.8 |
| 2854 | 2729 | * |
| 2855 | 2730 | * @param string $html HTML string. |
| 2856 | - * | |
| 2857 | 2731 | * @return string |
| 2858 | 2732 | */ |
| 2859 | 2733 | public function remove_price_if_enrolled( $html ) { |
| 2860 | 2734 | $should_removed = apply_filters( 'should_remove_price_if_enrolled', true ); |
| @@ -2860,9 +2734,9 @@ | ||
| 2860 | 2734 | $should_removed = apply_filters( 'should_remove_price_if_enrolled', true ); |
| 2861 | 2735 | |
| 2862 | 2736 | if ( $should_removed ) { |
| 2863 | 2737 | $course_id = get_the_ID(); |
| 2864 | - $enrolled = EnrollmentModel::is_enrolled( $course_id ); | |
| 2738 | + $enrolled = tutor_utils()->is_enrolled( $course_id ); | |
| 2865 | 2739 | if ( $enrolled ) { |
| 2866 | 2740 | $html = ''; |
| 2867 | 2741 | } |
| 2868 | 2742 | } |
| @@ -2869,119 +2743,112 @@ | ||
| 2869 | 2743 | return $html; |
| 2870 | 2744 | } |
| 2871 | 2745 | |
| 2872 | 2746 | /** |
| 2873 | - * Get course completion missing requirements message. | |
| 2747 | + * Check if all lessons and quizzes done before mark course complete. | |
| 2874 | 2748 | * |
| 2875 | - * @since 4.0.0 | |
| 2749 | + * @since 1.5.8 | |
| 2876 | 2750 | * |
| 2877 | - * @param int $course_id Course ID. | |
| 2878 | - * @param int $user_id User ID. | |
| 2879 | - * | |
| 2880 | - * @return string|null | |
| 2751 | + * @param string $html HTML string. | |
| 2752 | + * @return string | |
| 2881 | 2753 | */ |
| 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 ); | |
| 2754 | + public function tutor_lms_hide_course_complete_btn( $html ) { | |
| 2885 | 2755 | |
| 2886 | - if ( 'strict' !== tutor_utils()->get_option( 'course_completion_process' ) ) { | |
| 2887 | - return null; | |
| 2756 | + $completion_mode = tutor_utils()->get_option( 'course_completion_process' ); | |
| 2757 | + if ( 'strict' !== $completion_mode ) { | |
| 2758 | + return $html; | |
| 2888 | 2759 | } |
| 2889 | 2760 | |
| 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 ); | |
| 2761 | + $completed_lesson = tutor_utils()->get_completed_lesson_count_by_course(); | |
| 2762 | + $lesson_count = tutor_utils()->get_lesson_count_by_course(); | |
| 2892 | 2763 | |
| 2893 | - if ( $completed_lessons < $total_lessons ) { | |
| 2894 | - return __( 'Complete all lessons to mark this course as complete', 'tutor' ); | |
| 2764 | + if ( $completed_lesson < $lesson_count ) { | |
| 2765 | + return '<div class="tutor-alert tutor-warning tutor-mt-28"> | |
| 2766 | + <div class="tutor-alert-text"> | |
| 2767 | + <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span> | |
| 2768 | + <span>' . __( 'Complete all lessons to mark this course as complete', 'tutor' ) . '</span> | |
| 2769 | + </div> | |
| 2770 | + </div>'; | |
| 2895 | 2771 | } |
| 2896 | 2772 | |
| 2897 | - $course_contents = tutor_utils()->get_course_contents_by_id( $course_id ); | |
| 2773 | + $quizzes = array(); | |
| 2774 | + $assignments = array(); | |
| 2898 | 2775 | |
| 2899 | - $required_quiz_pass_count = 0; | |
| 2900 | - $required_assignment_pass_count = 0; | |
| 2776 | + $course_contents = tutor_utils()->get_course_contents_by_id(); | |
| 2777 | + if ( tutor_utils()->count( $course_contents ) ) { | |
| 2778 | + foreach ( $course_contents as $content ) { | |
| 2779 | + if ( 'tutor_quiz' === $content->post_type ) { | |
| 2780 | + $quizzes[] = $content; | |
| 2781 | + } | |
| 2782 | + if ( 'tutor_assignments' === $content->post_type ) { | |
| 2783 | + $assignments[] = $content; | |
| 2784 | + } | |
| 2785 | + } | |
| 2786 | + } | |
| 2901 | 2787 | |
| 2902 | - $is_assignment_addon_enabled = tutor_utils()->is_addon_enabled( 'tutor-assignments' ); | |
| 2788 | + $required_assignment_pass = 0; | |
| 2903 | 2789 | |
| 2904 | - foreach ( $course_contents as $content ) { | |
| 2790 | + foreach ( $assignments as $row ) { | |
| 2905 | 2791 | |
| 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 | - } | |
| 2792 | + $submitted_assignment = tutor_utils()->is_assignment_submitted( $row->ID ); | |
| 2793 | + $is_reviewed_by_instructor = null === $submitted_assignment | |
| 2794 | + ? false | |
| 2795 | + : get_comment_meta( $submitted_assignment->comment_ID, 'evaluate_time', true ); | |
| 2911 | 2796 | |
| 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; | |
| 2797 | + if ( $submitted_assignment && $is_reviewed_by_instructor ) { | |
| 2798 | + $pass_mark = tutor_utils()->get_assignment_option( $submitted_assignment->comment_post_ID, 'pass_mark' ); | |
| 2799 | + $given_mark = get_comment_meta( $submitted_assignment->comment_ID, 'assignment_mark', true ); | |
| 2800 | + | |
| 2801 | + if ( $given_mark < $pass_mark ) { | |
| 2802 | + $required_assignment_pass++; | |
| 2915 | 2803 | } |
| 2804 | + } else { | |
| 2805 | + $required_assignment_pass++; | |
| 2916 | 2806 | } |
| 2917 | 2807 | } |
| 2918 | 2808 | |
| 2919 | - if ( ! $required_quiz_pass_count && ! $required_assignment_pass_count ) { | |
| 2920 | - return null; | |
| 2921 | - } | |
| 2809 | + $is_quiz_pass = true; | |
| 2810 | + $required_quiz_pass = 0; | |
| 2922 | 2811 | |
| 2923 | - return self::get_course_completion_requirement_message( | |
| 2924 | - $required_quiz_pass_count, | |
| 2925 | - $required_assignment_pass_count | |
| 2926 | - ); | |
| 2927 | - } | |
| 2812 | + if ( tutor_utils()->count( $quizzes ) ) { | |
| 2813 | + foreach ( $quizzes as $quiz ) { | |
| 2928 | 2814 | |
| 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' ); | |
| 2815 | + $attempt = tutor_utils()->get_quiz_attempt( $quiz->ID ); | |
| 2816 | + if ( $attempt ) { | |
| 2817 | + $passing_grade = tutor_utils()->get_quiz_option( $quiz->ID, 'passing_grade', 0 ); | |
| 2818 | + $earned_percentage = QuizModel::calculate_attempt_earned_percentage( $attempt ); | |
| 2942 | 2819 | |
| 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 | - ); | |
| 2820 | + if ( $earned_percentage < $passing_grade ) { | |
| 2821 | + $required_quiz_pass++; | |
| 2822 | + $is_quiz_pass = false; | |
| 2823 | + } | |
| 2824 | + } else { | |
| 2825 | + $required_quiz_pass++; | |
| 2826 | + $is_quiz_pass = false; | |
| 2827 | + } | |
| 2828 | + } | |
| 2950 | 2829 | } |
| 2951 | 2830 | |
| 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 | - } | |
| 2831 | + if ( ! $is_quiz_pass || $required_assignment_pass > 0 ) { | |
| 2832 | + $_msg = ''; | |
| 2833 | + $quiz_str = _n( 'quiz', 'quizzes', $required_quiz_pass, 'tutor' ); | |
| 2834 | + $assignment_str = _n( 'assignment', 'assignments', $required_assignment_pass, 'tutor' ); | |
| 2960 | 2835 | |
| 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 | - } | |
| 2836 | + if ( ! $is_quiz_pass && 0 == $required_assignment_pass ) { | |
| 2837 | + /* translators: %1$s: number of quiz/assignment pass required; %2$s: quiz/assignment string */ | |
| 2838 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_quiz_pass, $quiz_str ); | |
| 2839 | + } | |
| 2970 | 2840 | |
| 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(); | |
| 2841 | + if ( $is_quiz_pass && $required_assignment_pass > 0 ) { | |
| 2842 | + //phpcs:ignore | |
| 2843 | + $_msg = sprintf( __( 'You have to pass %1$s %2$s to complete this course.', 'tutor' ), $required_assignment_pass, $assignment_str ); | |
| 2844 | + } | |
| 2982 | 2845 | |
| 2983 | - if ( $_msg ) { | |
| 2846 | + if ( ! $is_quiz_pass && $required_assignment_pass > 0 ) { | |
| 2847 | + /* translators: %1$s: number of quiz pass required; %2$s: quiz string; %3$s: number of assignment pass required; %4$s: assignment string */ | |
| 2848 | + $_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 ); | |
| 2849 | + } | |
| 2850 | + | |
| 2984 | 2851 | return '<div class="tutor-alert tutor-warning tutor-mt-28"> |
| 2985 | 2852 | <div class="tutor-alert-text"> |
| 2986 | 2853 | <span class="tutor-alert-icon tutor-fs-4 tutor-icon-circle-info tutor-mr-12"></span> |
| 2987 | 2854 | <span>' . $_msg . '</span> |
| @@ -2997,9 +2864,8 @@ | ||
| 2997 | 2864 | * |
| 2998 | 2865 | * @since 1.5.8 |
| 2999 | 2866 | * |
| 3000 | 2867 | * @param string $html HTML string. |
| 3001 | - * | |
| 3002 | 2868 | * @return string |
| 3003 | 2869 | */ |
| 3004 | 2870 | public function get_generate_greadbook( $html ) { |
| 3005 | 2871 | if ( ! tutor_utils()->is_completed_course() ) { |
| @@ -3011,9 +2877,8 @@ | ||
| 3011 | 2877 | /** |
| 3012 | 2878 | * Add social share content in header |
| 3013 | 2879 | * |
| 3014 | 2880 | * @since 1.6.3 |
| 3015 | - * | |
| 3016 | 2881 | * @return void |
| 3017 | 2882 | */ |
| 3018 | 2883 | public function social_share_content() { |
| 3019 | 2884 | global $wp_query, $post; |
| @@ -3037,9 +2902,8 @@ | ||
| 3037 | 2902 | * |
| 3038 | 2903 | * @since 1.8.2 |
| 3039 | 2904 | * |
| 3040 | 2905 | * @param integer $post_id post ID. |
| 3041 | - * | |
| 3042 | 2906 | * @return void |
| 3043 | 2907 | */ |
| 3044 | 2908 | public function delete_associated_enrollment( $post_id ) { |
| 3045 | 2909 | global $wpdb; |
| @@ -3050,12 +2914,11 @@ | ||
| 3050 | 2914 | post_id |
| 3051 | 2915 | FROM |
| 3052 | 2916 | {$wpdb->postmeta} |
| 3053 | 2917 | WHERE |
| 3054 | - meta_key=%s | |
| 2918 | + meta_key='_tutor_enrolled_by_order_id' | |
| 3055 | 2919 | AND meta_value = %d |
| 3056 | 2920 | ", |
| 3057 | - EnrollmentModel::ENROLLMENT_ORDER_ID_META, | |
| 3058 | 2921 | $post_id |
| 3059 | 2922 | ) |
| 3060 | 2923 | ); |
| 3061 | 2924 | |
| @@ -3071,35 +2934,21 @@ | ||
| 3071 | 2934 | /** |
| 3072 | 2935 | * Reset course progress. |
| 3073 | 2936 | * |
| 3074 | 2937 | * @since 1.5.8 |
| 3075 | - * | |
| 3076 | 2938 | * @return void |
| 3077 | 2939 | */ |
| 3078 | 2940 | public function tutor_reset_course_progress() { |
| 3079 | 2941 | 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 ); | |
| 2942 | + $course_id = Input::post( 'course_id' ); | |
| 3084 | 2943 | |
| 3085 | - if ( ! $course_reset_progress && 'learning-area-sidebar' === $context ) { | |
| 3086 | - $this->response_bad_request( __( 'You do not have permission to reset this course.', 'tutor' ) ); | |
| 2944 | + if ( ! $course_id || ! is_numeric( $course_id ) || ! tutor_utils()->is_enrolled( $course_id ) ) { | |
| 2945 | + wp_send_json_error( array( 'message' => __( 'Invalid Course ID or Access Denied.', 'tutor' ) ) ); | |
| 3087 | 2946 | return; |
| 3088 | 2947 | } |
| 3089 | 2948 | |
| 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 | 2949 | tutor_utils()->delete_course_progress( $course_id ); |
| 3101 | - $this->json_response( '', array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) ); | |
| 2950 | + wp_send_json_success( array( 'redirect_to' => tutor_utils()->get_course_first_lesson( $course_id ) ) ); | |
| 3102 | 2951 | } |
| 3103 | 2952 | |
| 3104 | 2953 | /** |
| 3105 | 2954 | * Do enroll if guest attempt to enroll and course is free |
| @@ -3107,9 +2956,9 @@ | ||
| 3107 | 2956 | * @since 1.9.8 |
| 3108 | 2957 | * |
| 3109 | 2958 | * @param integer $course_id course ID. |
| 3110 | 2959 | * @param integer $user_id user ID. |
| 3111 | - * | |
| 2960 | + | |
| 3112 | 2961 | * @return void |
| 3113 | 2962 | */ |
| 3114 | 2963 | public function enroll_after_login_if_attempt( int $course_id, int $user_id ) { |
| 3115 | 2964 | $course_id = sanitize_text_field( $course_id ); |
| @@ -3117,9 +2966,9 @@ | ||
| 3117 | 2966 | |
| 3118 | 2967 | if ( $course_id && $is_allowed ) { |
| 3119 | 2968 | $is_purchasable = tutor_utils()->is_course_purchasable( $course_id ); |
| 3120 | 2969 | if ( ! $is_purchasable ) { |
| 3121 | - EnrollmentModel::do_enroll( $course_id, $order_id = 0, $user_id ); | |
| 2970 | + tutor_utils()->do_enroll( $course_id, $order_id = 0, $user_id ); | |
| 3122 | 2971 | do_action( 'guest_attempt_after_enrollment', $course_id ); |
| 3123 | 2972 | } |
| 3124 | 2973 | } |
| 3125 | 2974 | } |
| @@ -3127,9 +2976,8 @@ | ||
| 3127 | 2976 | /** |
| 3128 | 2977 | * Handle course enrollment |
| 3129 | 2978 | * |
| 3130 | 2979 | * @since 2.1.0 |
| 3131 | - * | |
| 3132 | 2980 | * @return void |
| 3133 | 2981 | */ |
| 3134 | 2982 | public function course_enrollment() { |
| 3135 | 2983 | tutor_utils()->checking_nonce(); |
| @@ -3136,46 +2984,21 @@ | ||
| 3136 | 2984 | |
| 3137 | 2985 | $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); |
| 3138 | 2986 | $user_id = get_current_user_id(); |
| 3139 | 2987 | |
| 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' ) ); | |
| 2988 | + if ( $course_id ) { | |
| 2989 | + $password_protected = post_password_required( $course_id ); | |
| 2990 | + if ( $password_protected ) { | |
| 2991 | + wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); | |
| 3170 | 2992 | } |
| 3171 | - } | |
| 3172 | - | |
| 3173 | - $enroll = EnrollmentModel::do_enroll( $course_id, 0, $user_id ); | |
| 3174 | - if ( $enroll ) { | |
| 3175 | - wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); | |
| 2993 | + $enroll = tutor_utils()->do_enroll( $course_id, 0, $user_id ); | |
| 2994 | + if ( $enroll ) { | |
| 2995 | + wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); | |
| 2996 | + } else { | |
| 2997 | + wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); | |
| 2998 | + } | |
| 3176 | 2999 | } else { |
| 3177 | - wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); | |
| 3000 | + wp_send_json_error( __( 'Invalid course ID', 'tutor' ) ); | |
| 3178 | 3001 | } |
| 3179 | 3002 | } |
| 3180 | 3003 | |
| 3181 | 3004 | /** |
| @@ -3262,10 +3085,14 @@ | ||
| 3262 | 3085 | 'regular_price' => $regular_price, |
| 3263 | 3086 | 'sale_price' => $sale_price, |
| 3264 | 3087 | ); |
| 3265 | 3088 | |
| 3266 | - $card_data = apply_filters( 'tutor_course_mini_info', $info, $post ); | |
| 3089 | + if ( 'course-bundle' === $post->post_type && tutor_utils()->is_addon_enabled( 'tutor-pro/addons/course-bundle/course-bundle.php' ) ) { | |
| 3090 | + $info['total_course'] = count( BundleModel::get_bundle_course_ids( $post->ID ) ); | |
| 3091 | + } | |
| 3267 | 3092 | |
| 3093 | + $card_data = apply_filters( 'tutor_add_course_plan_info', $info, $post ); | |
| 3094 | + | |
| 3268 | 3095 | return $card_data; |
| 3269 | 3096 | } |
| 3270 | 3097 | |
| 3271 | 3098 | /** |
| @@ -3286,9 +3113,9 @@ | ||
| 3286 | 3113 | $info['last_updated'] = tutor_i18n_get_formated_date( $post->post_modified_at ); |
| 3287 | 3114 | $info['course_duration'] = tutor_utils()->get_course_duration( $post->ID, false ); |
| 3288 | 3115 | $info['total_enrolled'] = tutor_utils()->count_enrolled_users_by_course( $post->ID ); |
| 3289 | 3116 | |
| 3290 | - $card_data = apply_filters( 'tutor_course_card_data', $info, $post ); | |
| 3117 | + $card_data = apply_filters( 'tutor_add_course_plan_info', $info, $post ); | |
| 3291 | 3118 | |
| 3292 | 3119 | return $card_data; |
| 3293 | 3120 | } |
| 3294 | 3121 | |
| @@ -3345,199 +3172,6 @@ | ||
| 3345 | 3172 | 'trash', |
| 3346 | 3173 | 'pending', |
| 3347 | 3174 | 'future', |
| 3348 | 3175 | ); |
| 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 | 3176 | } |
| 3543 | 3177 | } |