diff --git a/assets/src/js/front/tutor-front.js b/assets/src/js/front/tutor-front.js index 47f54802fc..ac1229e3d5 100644 --- a/assets/src/js/front/tutor-front.js +++ b/assets/src/js/front/tutor-front.js @@ -260,6 +260,11 @@ jQuery(document).ready(function($) { return; } + // If user is not enrolled like lesson preview. + if (!video_data.is_enrolled) { + return; + } + if (this.isRequiredPercentage()) { this.enable_complete_lesson_btn(instance); } diff --git a/classes/Addons.php b/classes/Addons.php index bde10328ae..7604efb225 100644 --- a/classes/Addons.php +++ b/classes/Addons.php @@ -10,9 +10,8 @@ namespace TUTOR; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} +defined( 'ABSPATH' ) || exit; + /** * Addons Class * @@ -25,6 +24,7 @@ class Addons { * Constructor * * @since 1.0.0 + * * @return void */ public function __construct() { @@ -52,6 +52,7 @@ public static function get_addons_config() { * * @param string $basename basename of addon. * @param bool $status status 0,1. + * * @return void */ public static function update_addon_status( $basename, $status ) { @@ -66,6 +67,7 @@ public static function update_addon_status( $basename, $status ) { * Get all addons data. * * @since 1.0.0 + * * @return void */ public function get_all_addons() { @@ -73,7 +75,7 @@ public function get_all_addons() { // Check and verify the request. tutor_utils()->checking_nonce(); - if ( ! User::is_admin() ) { + if ( ! User::can() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -179,8 +181,8 @@ public function addon_enable_disable() { tutor_utils()->checking_nonce(); - if ( ! current_user_can( 'manage_options' ) ) { - wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) ); + if ( ! User::can() ) { + wp_send_json_error( tutor_utils()->error_message() ); } $form_data = json_decode( Input::post( 'addonFieldNames' ) ); @@ -227,6 +229,7 @@ public function addon_enable_disable() { * Get tutor addons list * * @since 1.0.0 + * * @return array */ public function addons_lists_to_show() { diff --git a/classes/Admin.php b/classes/Admin.php index 09bb21814a..7d1d609c60 100644 --- a/classes/Admin.php +++ b/classes/Admin.php @@ -10,16 +10,14 @@ namespace TUTOR; +defined( 'ABSPATH' ) || exit; + use Tutor\Ecommerce\OrderController; use Tutor\Helpers\HttpHelper; use TUTOR\Input; use Tutor\Models\CourseModel; use Tutor\Traits\JsonResponse; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - /** * Admin Class * diff --git a/classes/Ajax.php b/classes/Ajax.php index 993986268e..f26b171a5d 100644 --- a/classes/Ajax.php +++ b/classes/Ajax.php @@ -10,9 +10,7 @@ namespace TUTOR; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} +defined( 'ABSPATH' ) || exit; use Tutor\GDPR\Controllers\LegalConsent; use Tutor\Helpers\HttpHelper; @@ -28,6 +26,7 @@ class Ajax { use JsonResponse; const LOGIN_ERRORS_TRANSIENT_KEY = 'tutor_login_errors'; + /** * Constructor * @@ -41,7 +40,6 @@ class Ajax { public function __construct( $allow_hooks = true ) { if ( $allow_hooks ) { add_action( 'wp_ajax_sync_video_playback', array( $this, 'sync_video_playback' ) ); - add_action( 'wp_ajax_nopriv_sync_video_playback', array( $this, 'sync_video_playback_noprev' ) ); add_action( 'wp_ajax_tutor_place_rating', array( $this, 'tutor_place_rating' ) ); add_action( 'wp_ajax_delete_tutor_review', array( $this, 'delete_tutor_review' ) ); @@ -60,8 +58,8 @@ public function __construct( $allow_hooks = true ) { * * @since v.1.7.9 */ - add_action( 'wp_ajax_tutor_announcement_create', array( $this, 'create_or_update_annoucement' ) ); - add_action( 'wp_ajax_tutor_announcement_delete', array( $this, 'delete_annoucement' ) ); + add_action( 'wp_ajax_tutor_announcement_create', array( $this, 'create_or_update_announcement' ) ); + add_action( 'wp_ajax_tutor_announcement_delete', array( $this, 'delete_announcement' ) ); add_action( 'wp_ajax_tutor_youtube_video_duration', array( $this, 'ajax_youtube_video_duration' ) ); } @@ -73,6 +71,7 @@ public function __construct( $allow_hooks = true ) { * Update video information and data when necessary * * @since 1.0.0 + * * @return void */ public function sync_video_playback() { @@ -116,15 +115,6 @@ public function sync_video_playback() { exit(); } - /** - * Video playback callback for noprev - * - * @since 1.0.0 - * @return void - */ - public function sync_video_playback_noprev() { - } - /** * Place rating * @@ -396,9 +386,7 @@ public function add_or_delete_wishlist( $user_id, $course_id ) { * Process tutor login * * @since 1.6.3 - * - * @since 2.1.3 Ajax removed, validation errors - * stores in session. + * @since 2.1.3 Ajax removed, validation errors stores in session. * * @return void */ @@ -411,9 +399,9 @@ public function process_tutor_login() { * * @since 2.1.4 */ - if ( ! wp_verify_nonce( $_POST[ tutor()->nonce ], tutor()->nonce_action ) ) { //phpcs:ignore + if ( ! tutor_utils()->is_nonce_verified( 'post' ) ) { $validation_error->add( 401, __( 'Nonce verification failed', 'tutor' ) ); - \set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages() ); + \set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages(), MINUTE_IN_SECONDS ); return; } @@ -425,10 +413,12 @@ public function process_tutor_login() { * * @see https://developer.wordpress.org/reference/functions/wp_signon/ */ - $username = tutor_utils()->array_get( 'log', $_POST ); //phpcs:ignore - $password = tutor_utils()->array_get( 'pwd', $_POST ); //phpcs:ignore + //phpcs:disable WordPress.Security.NonceVerification.Missing + $username = tutor_utils()->array_get( 'log', $_POST ); //phpcs:ignore + $password = tutor_utils()->array_get( 'pwd', $_POST ); //phpcs:ignore $redirect_to = isset( $_POST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_to'] ) ) : ''; $remember = isset( $_POST['rememberme'] ); + //phpcs:enable WordPress.Security.NonceVerification.Missing try { $creds = array( @@ -497,7 +487,7 @@ public function process_tutor_login() { $validation_error->add( 400, $e->getMessage() ); } finally { // Store errors in transient data. - \set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages() ); + \set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages(), MINUTE_IN_SECONDS ); } } @@ -505,9 +495,10 @@ public function process_tutor_login() { * Create/Update announcement * * @since 1.7.9 + * * @return void */ - public function create_or_update_annoucement() { + public function create_or_update_announcement() { tutor_utils()->checking_nonce(); $error = array(); @@ -515,7 +506,7 @@ public function create_or_update_annoucement() { $announcement_title = Input::post( 'tutor_announcement_title' ); $announcement_summary = Input::post( 'tutor_announcement_summary', '', Input::TYPE_TEXTAREA ); - // Check if user can manage this announcment. + // Check if user can manage this announcement. if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) ) { wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) ); } @@ -588,9 +579,10 @@ public function create_or_update_annoucement() { * Delete announcement * * @since 1.7.9 + * * @return void */ - public function delete_annoucement() { + public function delete_announcement() { tutor_utils()->checking_nonce(); $announcement_id = Input::post( 'announcement_id' ); diff --git a/classes/Announcements.php b/classes/Announcements.php index a3b9c84f9e..b51d9156a5 100644 --- a/classes/Announcements.php +++ b/classes/Announcements.php @@ -10,12 +10,11 @@ namespace TUTOR; +defined( 'ABSPATH' ) || exit; + use Tutor\Helpers\QueryHelper; use Tutor\Helpers\UrlHelper; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} /** * Announcements class * @@ -41,6 +40,7 @@ class Announcements { * Constructor * * @since 1.0.0 + * * @return void */ public function __construct() { @@ -74,6 +74,7 @@ public function __get( $name ) { * Prepare bulk actions that will show on dropdown options * * @since 2.0.0 + * * @return array */ public function prepare_bulk_actions(): array { @@ -88,6 +89,7 @@ public function prepare_bulk_actions(): array { * Handle bulk action for enrollment cancel | delete * * @since 2.0.0 + * * @return string JSON response. */ public function announcement_bulk_action() { @@ -118,7 +120,7 @@ function ( $announcement_id ) { * @since 2.0.0 * * @param string $action hold action. - * @param string $bulk_ids comma seperated ids. + * @param string $bulk_ids comma separated ids. * * @return bool */ diff --git a/classes/Course.php b/classes/Course.php index 196c1766c1..0b8454b9de 100644 --- a/classes/Course.php +++ b/classes/Course.php @@ -10,9 +10,7 @@ namespace TUTOR; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} +defined( 'ABSPATH' ) || exit; use Tutor\Components\Button; use Tutor\Components\Constants\Size; @@ -935,13 +933,12 @@ public function ajax_create_new_draft_course() { * * @since 3.0.0 * - * @since 3.2.0 - * - * Refactor the arguments & response as per new design + * @since 3.2.0 Refactor the arguments & response as per new design. * * @return void */ public function ajax_course_list() { + tutor_utils()->check_nonce(); $this->check_access(); $limit = Input::post( 'limit', 10, Input::TYPE_INT ); @@ -963,12 +960,7 @@ public function ajax_course_list() { $exclude = Input::post( 'exclude', array(), Input::TYPE_ARRAY ); if ( count( $exclude ) ) { - $exclude = array_filter( - $exclude, - function ( $id ) { - return is_numeric( $id ); - } - ); + $exclude = array_filter( $exclude, fn( $id ) => is_numeric( $id ) && $id > 0 ); $args['post__not_in'] = $exclude; } @@ -1110,6 +1102,10 @@ public function ajax_update_course() { ); $course_id = (int) $params['course_id']; + if ( ! $course_id ) { + $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); + } + $this->check_access( $course_id ); $errors = array(); @@ -1200,9 +1196,13 @@ public function ajax_unlink_page_builder() { tutor_utils()->check_nonce(); $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); - $builder = Input::post( 'builder' ); + if ( ! $course_id ) { + $this->response_bad_request( __( 'Invalid course id', 'tutor' ) ); + } + $this->check_access( $course_id ); + $builder = Input::post( 'builder' ); if ( 'elementor' === $builder ) { delete_post_meta( $course_id, '_elementor_edit_mode' ); } elseif ( 'droip' === $builder ) { @@ -1277,18 +1277,21 @@ public function get_course_contents( $course_id ) { * Get course contents * * @since 3.0.0 + * + * @return void */ public function ajax_course_contents() { tutor_utils()->check_nonce(); + $errors = array(); $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); - $this->check_access( $course_id ); - - if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { + if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { $errors['course_id'] = __( 'Invalid course id', 'tutor' ); } + $this->check_access( $course_id ); + if ( ! empty( $errors ) ) { $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); } @@ -1314,12 +1317,12 @@ public function ajax_course_details() { $errors = array(); $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); - $this->check_access( $course_id ); - - if ( tutor()->course_post_type !== get_post_type( $course_id ) ) { + if ( ! $course_id || tutor()->course_post_type !== get_post_type( $course_id ) ) { $errors['course_id'] = __( 'Invalid course id', 'tutor' ); } + $this->check_access( $course_id ); + if ( ! empty( $errors ) ) { $this->json_response( __( 'Invalid input', 'tutor' ), $errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); } @@ -1332,7 +1335,7 @@ public function ajax_course_details() { $sale_price = 0; $product_id = tutor_utils()->get_course_product_id( $course_id ); - if ( 'wc' === $monetize_by ) { + if ( WooCommerce::MONETIZE_BY === $monetize_by ) { $product = wc_get_product( $product_id ); if ( $product ) { $product_name = $product->get_name(); @@ -1341,7 +1344,7 @@ public function ajax_course_details() { } } - if ( 'tutor' === $monetize_by ) { + if ( Ecommerce::MONETIZE_BY === $monetize_by ) { $price = get_post_meta( $course_id, self::COURSE_PRICE_META, true ); $sale_price = get_post_meta( $course_id, self::COURSE_SALE_PRICE_META, true ); } @@ -1794,7 +1797,9 @@ public function restrict_new_student_entry( $content ) { * Restrict media * * @since 1.0.0 + * * @param string $where where clause. + * * @return string */ public function restrict_media( $where ) { @@ -2341,6 +2346,7 @@ public function clear_review_popup_data() { * Delete course delete from frontend dashboard * * @since 2.0.0 + * * @return void */ public function tutor_delete_dashboard_course() { @@ -2361,7 +2367,7 @@ public function tutor_delete_dashboard_course() { } // Check if user is only an instructor. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { // Check if instructor can trash course. $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ); @@ -2373,7 +2379,7 @@ public function tutor_delete_dashboard_course() { $trash_course = wp_update_post( array( 'ID' => $course_id, - 'post_status' => 'trash', + 'post_status' => CourseModel::STATUS_TRASH, ) ); @@ -2571,6 +2577,7 @@ public function tutor_lesson_load_before() { * Add Course level to course settings * * @since 1.4.8 + * * @return void */ public function course_elements_enable_disable() { @@ -2605,6 +2612,7 @@ public function enable_disable_course_progress_bar( $html ) { * @since 1.4.8 * * @param string $html HTML string. + * * @return string */ public function enable_disable_material_includes( $html ) { @@ -2621,6 +2629,7 @@ public function enable_disable_material_includes( $html ) { * @since 1.4.8 * * @param string $html HTML string. + * * @return string */ public function enable_disable_course_content( $html ) { @@ -2637,6 +2646,7 @@ public function enable_disable_course_content( $html ) { * @since 1.4.8 * * @param string $html HTML string. + * * @return string */ public function enable_disable_course_benefits( $html ) { @@ -2653,6 +2663,7 @@ public function enable_disable_course_benefits( $html ) { * @since 1.4.8 * * @param string $html HTML string. + * * @return string */ public function enable_disable_course_requirements( $html ) { @@ -2669,6 +2680,7 @@ public function enable_disable_course_requirements( $html ) { * @since 1.4.8 * * @param string $html HTML string. + * * @return string */ public function enable_disable_course_target_audience( $html ) { @@ -2732,6 +2744,7 @@ function ( $item ) use ( $is_enrolled ) { * Filter product in shop page * * @since 1.4.9 + * * @return void|null */ public function filter_product_in_shop_page() { @@ -2749,6 +2762,7 @@ public function filter_product_in_shop_page() { * Tutor product meta query * * @since 1.4.9 + * * @return array */ public function tutor_product_meta_query() { @@ -2765,6 +2779,7 @@ public function tutor_product_meta_query() { * @since 1.4.9 * * @param \WP_Query $wp_query WP Query instance. + * * @return \WP_Query */ public function filter_woocommerce_product_query( $wp_query ) { @@ -2808,6 +2823,7 @@ public function get_connected_wc_product_ids() { * @since 1.4.9 * * @param \WP_Query $query WP Query instance. + * * @return \WP_Query */ public function filter_edd_downloads_query( $query ) { @@ -2821,6 +2837,7 @@ public function filter_edd_downloads_query( $query ) { * @since 1.4.9 * * @param \WP_Query $wp_query WP Query instance. + * * @return \WP_Query */ public function filter_archive_meta_query( $wp_query ) { @@ -2836,6 +2853,7 @@ public function filter_archive_meta_query( $wp_query ) { * @since 1.5.8 * * @param string $html HTML string. + * * @return string */ public function remove_price_if_enrolled( $html ) { @@ -2956,6 +2974,7 @@ private static function get_course_completion_requirement_message( $quiz_count, * @since 1.5.8 * * @param string $html HTML string. + * * @return string */ public function tutor_lms_hide_course_complete_btn( $html ) { @@ -2979,6 +2998,7 @@ public function tutor_lms_hide_course_complete_btn( $html ) { * @since 1.5.8 * * @param string $html HTML string. + * * @return string */ public function get_generate_greadbook( $html ) { @@ -2992,6 +3012,7 @@ public function get_generate_greadbook( $html ) { * Add social share content in header * * @since 1.6.3 + * * @return void */ public function social_share_content() { @@ -3017,6 +3038,7 @@ public function social_share_content() { * @since 1.8.2 * * @param integer $post_id post ID. + * * @return void */ public function delete_associated_enrollment( $post_id ) { @@ -3050,6 +3072,7 @@ public function delete_associated_enrollment( $post_id ) { * Reset course progress. * * @since 1.5.8 + * * @return void */ public function tutor_reset_course_progress() { @@ -3085,7 +3108,7 @@ public function tutor_reset_course_progress() { * * @param integer $course_id course ID. * @param integer $user_id user ID. - + * * @return void */ public function enroll_after_login_if_attempt( int $course_id, int $user_id ) { @@ -3105,6 +3128,7 @@ public function enroll_after_login_if_attempt( int $course_id, int $user_id ) { * Handle course enrollment * * @since 2.1.0 + * * @return void */ public function course_enrollment() { @@ -3113,44 +3137,44 @@ public function course_enrollment() { $course_id = Input::post( 'course_id', 0, Input::TYPE_INT ); $user_id = get_current_user_id(); - if ( $course_id ) { - $password_protected = post_password_required( $course_id ); - if ( $password_protected ) { - wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); - } + if ( ! $course_id || ! $user_id ) { + wp_send_json_error( tutor_utils()->error_message( 'invalid_req' ) ); + } - $course = get_post( $course_id ); + $password_protected = post_password_required( $course_id ); + if ( $password_protected ) { + wp_send_json_error( __( 'This course is password protected', 'tutor' ) ); + } - if ( 'private' === $course->post_status && ! current_user_can( 'read_private_tutor_courses' ) ) { - wp_send_json_error( __( 'You do not have permission to enroll in this course', 'tutor' ) ); - } + $course = get_post( $course_id ); - /** - * This check was added to address a security issue where users could - * enroll in a course via an AJAX call without purchasing it. - * - * To prevent this, we now verify whether the course is paid. - * Additionally, we check if the user is already enrolled, since - * Tutor's default behavior enrolls users automatically upon purchase. - * - * @since 3.9.4 - */ - if ( tutor_utils()->is_course_purchasable( $course_id ) ) { - $is_enrolled = (bool) EnrollmentModel::is_enrolled( $course_id, $user_id ); - - if ( ! $is_enrolled ) { - wp_send_json_error( __( 'Please purchase the course before enrolling', 'tutor' ) ); - } - } + if ( CourseModel::STATUS_PRIVATE === $course->post_status && ! current_user_can( 'read_private_tutor_courses' ) ) { + wp_send_json_error( __( 'You do not have permission to enroll in this course', 'tutor' ) ); + } - $enroll = EnrollmentModel::do_enroll( $course_id, 0, $user_id ); - if ( $enroll ) { - wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); - } else { - wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); + /** + * This check was added to address a security issue where users could + * enroll in a course via an AJAX call without purchasing it. + * + * To prevent this, we now verify whether the course is paid. + * Additionally, we check if the user is already enrolled, since + * Tutor's default behavior enrolls users automatically upon purchase. + * + * @since 3.9.4 + */ + if ( tutor_utils()->is_course_purchasable( $course_id ) ) { + $is_enrolled = (bool) EnrollmentModel::is_enrolled( $course_id, $user_id ); + + if ( ! $is_enrolled ) { + wp_send_json_error( __( 'Please purchase the course before enrolling', 'tutor' ) ); } + } + + $enroll = EnrollmentModel::do_enroll( $course_id, 0, $user_id ); + if ( $enroll ) { + wp_send_json_success( __( 'Enrollment successfully done!', 'tutor' ) ); } else { - wp_send_json_error( __( 'Invalid course ID', 'tutor' ) ); + wp_send_json_error( __( 'Enrollment failed, please try again!', 'tutor' ) ); } } @@ -3331,6 +3355,7 @@ public static function course_status_list() { * * @param int $post_ID The WordPress post ID of the course. * @param int $product_id The WooCommerce product ID to associate with the course. + * * @return void */ public static function sync_course_with_wc_product( $post_ID, $product_id ) { diff --git a/classes/Course_Filter.php b/classes/Course_Filter.php index 18bb06a558..e013929c59 100644 --- a/classes/Course_Filter.php +++ b/classes/Course_Filter.php @@ -47,6 +47,7 @@ class Course_Filter { * @since 1.0.0 * * @param boolean $register_hook register hook or not. + * * @return void|null */ public function __construct( $register_hook = true ) { @@ -55,7 +56,7 @@ public function __construct( $register_hook = true ) { } add_action( 'wp_ajax_tutor_course_filter_ajax', array( $this, 'load_listing' ), 10, 0 ); add_action( 'wp_ajax_nopriv_tutor_course_filter_ajax', array( $this, 'load_listing' ), 10, 0 ); - add_filter( 'term_link', __CLASS__ . '::filter_course_category_term_link', 10, 3 ); + add_filter( 'term_link', array( $this, 'filter_course_category_term_link' ), 10, 3 ); } /** @@ -64,7 +65,7 @@ public function __construct( $register_hook = true ) { * @since 1.0.0 * * @param mixed $filters filters. - * @param boolean $return_filter return filterd data or not. + * @param boolean $return_filter return filtered data or not. * @return mixed */ public function load_listing( $filters = null, $return_filter = false ) { @@ -122,26 +123,14 @@ public function load_listing( $filters = null, $return_filter = false ) { ); $post_ids_array = tutils()->array_get( 'tutor-course-filter-post-ids', $sanitized_post, array() ); - - $post_ids_array = array_map( - function ( $post_id ) { - return (int) $post_id; - }, - $post_ids_array - ); + $post_ids_array = array_map( 'intval', $post_ids_array ); if ( count( $post_ids_array ) ) { $args['post__in'] = $post_ids_array; } $exclude_ids_array = tutils()->array_get( 'tutor-course-filter-exclude-ids', $sanitized_post, array() ); - - $exclude_ids_array = array_map( - function ( $exclude_id ) { - return (int) $exclude_id; - }, - $exclude_ids_array - ); + $exclude_ids_array = array_map( 'intval', $exclude_ids_array ); if ( count( $exclude_ids_array ) ) { $args['post__not_in'] = $exclude_ids_array; @@ -152,12 +141,7 @@ function ( $exclude_id ) { $term_array = tutils()->array_get( 'tutor-course-filter-' . $taxonomy, $sanitized_post, array() ); ! is_array( $term_array ) ? $term_array = array( $term_array ) : 0; - $term_array = array_filter( - $term_array, - function( $term_id ) { - return is_numeric( $term_id ); - } - ); + $term_array = array_filter( $term_array, fn ( $term_id ) => is_numeric( $term_id ) ); if ( count( $term_array ) > 0 ) { $tax_query = array( @@ -288,6 +272,7 @@ private function sort_terms_hierarchically( $terms, $parent_id = 0 ) { * * @param array $terms term list. * @param string $taxonomy taxonomy name. + * * @return void */ private function render_terms_hierarchically( $terms, $taxonomy ) { @@ -312,6 +297,7 @@ private function render_terms_hierarchically( $terms, $taxonomy ) { * @since 1.0.0 * * @param string $taxonomy taxonomy name. + * * @return void */ public function render_terms( $taxonomy ) { diff --git a/classes/Course_List.php b/classes/Course_List.php index 6d0d0051d0..9400b7b372 100644 --- a/classes/Course_List.php +++ b/classes/Course_List.php @@ -10,12 +10,11 @@ namespace TUTOR; +defined( 'ABSPATH' ) || exit; + use Tutor\Helpers\QueryHelper; use Tutor\Models\CourseModel; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} /** * Course List class * @@ -41,25 +40,26 @@ class Course_List { * Constructor * * @return void + * * @since 2.0.0 */ public function __construct() { /** * Handle bulk action * - * @since v2.0.0 + * @since 2.0.0 */ add_action( 'wp_ajax_tutor_course_list_bulk_action', array( $this, 'course_list_bulk_action' ) ); /** * Handle ajax request for updating course status * - * @since v2.0.0 + * @since 2.0.0 */ add_action( 'wp_ajax_tutor_change_course_status', array( $this, 'tutor_change_course_status' ) ); /** * Handle ajax request for delete course * - * @since v2.0.0 + * @since 2.0.0 */ add_action( 'wp_ajax_tutor_course_delete', array( $this, 'tutor_course_delete' ) ); } @@ -82,8 +82,9 @@ public function __get( $name ) { /** * Prepare bulk actions that will show on dropdown options * - * @return array * @since 2.0.0 + * + * @return array */ public function prepare_bulk_actions(): array { $actions = array( @@ -95,22 +96,17 @@ public function prepare_bulk_actions(): array { $active_tab = Input::get( 'data', '' ); - if ( 'trash' === $active_tab ) { + if ( CourseModel::STATUS_TRASH === $active_tab ) { array_push( $actions, $this->bulk_action_delete() ); } - if ( 'trash' !== $active_tab ) { + if ( CourseModel::STATUS_TRASH !== $active_tab ) { array_push( $actions, $this->bulk_action_trash() ); } - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { $can_trash_post = tutor_utils()->get_option( 'instructor_can_delete_course' ) && current_user_can( 'edit_tutor_course' ); if ( ! $can_trash_post ) { - $actions = array_filter( - $actions, - function ( $val ) { - return 'trash' !== $val['value']; - } - ); + $actions = array_filter( $actions, fn ( $val ) => CourseModel::STATUS_TRASH !== $val['value'] ); } } return apply_filters( 'tutor_course_bulk_actions', $actions ); @@ -119,14 +115,14 @@ function ( $val ) { /** * Available tabs that will visible on the right side of page navbar * + * @since 2.0.0 + * * @param string $category_slug category slug. * @param integer $course_id course ID. * @param string $date selected date | optional. * @param string $search search by user name or email | optional. * * @return array - * - * @since v2.0.0 */ public function tabs_key_value( $category_slug, $course_id, $date, $search ): array { $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link() ); @@ -200,6 +196,8 @@ public function tabs_key_value( $category_slug, $course_id, $date, $search ): ar * Count courses by status & filters * Count all | min | published | pending | draft * + * @since 2.0.0 + * * @param string $status | required. * @param string $category_slug course category | optional. * @param string $course_id selected course id | optional. @@ -207,8 +205,6 @@ public function tabs_key_value( $category_slug, $course_id, $date, $search ): ar * @param string $search_term search by user name or email | optional. * * @return int - * - * @since 2.0.0 */ protected static function count_course( string $status, $category_slug = '', $course_id = '', $date = '', $search_term = '' ): int { $user_id = get_current_user_id(); @@ -235,9 +231,9 @@ protected static function count_course( string $status, $category_slug = '', $co $date_filter = sanitize_text_field( $date ); - $year = date( 'Y', strtotime( $date_filter ) ); - $month = date( 'm', strtotime( $date_filter ) ); - $day = date( 'd', strtotime( $date_filter ) ); + $year = gmdate( 'Y', strtotime( $date_filter ) ); + $month = gmdate( 'm', strtotime( $date_filter ) ); + $day = gmdate( 'd', strtotime( $date_filter ) ); // Add date query. if ( '' !== $date_filter ) { @@ -278,8 +274,9 @@ protected static function count_course( string $status, $category_slug = '', $co /** * Handle bulk action for enrollment cancel | delete * - * @return void * @since 2.0.0 + * + * @return void */ public function course_list_bulk_action() { @@ -289,13 +286,13 @@ public function course_list_bulk_action() { $bulk_ids = Input::post( 'bulk-ids', '' ); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { $course_ids = explode( ',', $bulk_ids ); if ( current_user_can( 'edit_tutor_course' ) ) { $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' ); - if ( 'publish' === $action && ! $can_publish_course ) { + if ( CourseModel::STATUS_PUBLISH === $action && ! $can_publish_course ) { wp_send_json_error( tutor_utils()->error_message() ); } } else { @@ -350,18 +347,19 @@ function ( $course_id ) { /** * Handle ajax request for updating course status * - * @return void * @since 2.0.0 + * + * @return void */ public static function tutor_change_course_status() { tutor_utils()->checking_nonce(); $status = Input::post( 'status' ); - $id = Input::post( 'id' ); + $id = Input::post( 'id', 0, Input::TYPE_INT ); $course = get_post( $id ); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { if ( ! tutor_utils()->can_user_edit_course( get_current_user_id(), $course->ID ) ) { wp_send_json_error( tutor_utils()->error_message() ); @@ -370,11 +368,11 @@ public static function tutor_change_course_status() { $can_delete_course = tutor_utils()->get_option( 'instructor_can_delete_course' ); $can_publish_course = tutor_utils()->get_option( 'instructor_can_publish_course' ); - if ( 'publish' === $status && ! $can_publish_course ) { + if ( CourseModel::STATUS_PUBLISH === $status && ! $can_publish_course ) { wp_send_json_error( tutor_utils()->error_message() ); } - if ( 'trash' === $status && $can_delete_course ) { + if ( CourseModel::STATUS_TRASH === $status && $can_delete_course ) { $args = array( 'ID' => $id, 'post_status' => $status, @@ -439,9 +437,11 @@ public static function tutor_course_delete() { /** * Execute bulk delete action * + * @since 2.0.0 + * * @param string $bulk_ids ids that need to update. + * * @return bool - * @since 2.0.0 */ public static function bulk_delete_course( $bulk_ids ): bool { $bulk_ids = explode( ',', sanitize_text_field( $bulk_ids ) ); @@ -456,12 +456,12 @@ public static function bulk_delete_course( $bulk_ids ): bool { /** * Update course status * + * @since 2.0.0 + * * @param string $status for updating course status. * @param string $bulk_ids comma separated ids. * * @return bool - * - * @since 2.0.0 */ public static function update_course_status( string $status, $bulk_ids ): bool { global $wpdb; @@ -472,7 +472,7 @@ public static function update_course_status( string $status, $bulk_ids ): bool { $ids = array_map( 'intval', explode( ',', $bulk_ids ) ); $in_clause = QueryHelper::prepare_in_clause( $ids ); - $update = $wpdb->query( + $wpdb->query( $wpdb->prepare( "UPDATE {$post_table} SET post_status = %s WHERE ID IN ($in_clause)", //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $status @@ -483,57 +483,13 @@ public static function update_course_status( string $status, $bulk_ids ): bool { } /** - * Get course enrollment list with student info + * Check course is public or not * - * @param int $course_id int | required. - * @return array - * @since 2.0.0 - */ - public static function course_enrollments_with_student_details( int $course_id ) { - global $wpdb; - $course_id = sanitize_text_field( $course_id ); - $course_completed = 0; - $course_inprogress = 0; - - $enrollments = $wpdb->get_results( - $wpdb->prepare( - "SELECT enroll.ID AS enroll_id, enroll.post_author AS enroll_author, user.*, course.ID AS course_id - FROM {$wpdb->posts} AS enroll - LEFT JOIN {$wpdb->users} AS user ON user.ID = enroll.post_author - LEFT JOIN {$wpdb->posts} AS course ON course.ID = enroll.post_parent - WHERE enroll.post_type = %s - AND enroll.post_status = %s - AND enroll.post_parent = %d - ", - 'tutor_enrolled', - 'completed', - $course_id - ) - ); - - foreach ( $enrollments as $enrollment ) { - $course_progress = tutor_utils()->get_course_completed_percent( $course_id, $enrollment->enroll_author ); - if ( 100 == $course_progress ) { - $course_completed++; - } else { - $course_inprogress++; - } - } - - return array( - 'enrollments' => $enrollments, - 'total_completed' => $course_completed, - 'total_inprogress' => $course_inprogress, - 'total_enrollments' => count( $enrollments ), - ); - } - - /** - * Check wheather course is public or not + * @since 1.0.0 * * @param integer $course_id course id to check with. + * * @return boolean true if public otherwise false. - * @since 1.0.0 */ public static function is_public( int $course_id ): bool { $is_public = get_post_meta( $course_id, '_tutor_is_public_course', true ); diff --git a/classes/Course_Settings_Tabs.php b/classes/Course_Settings_Tabs.php index 4a79b46f51..d298a29590 100644 --- a/classes/Course_Settings_Tabs.php +++ b/classes/Course_Settings_Tabs.php @@ -15,7 +15,7 @@ } /** - * Course Settings Tabls Class + * Course Settings Tabs Class * * @since 2.0.0 */ diff --git a/classes/Custom_Validation.php b/classes/Custom_Validation.php index 2ebdd16e59..b55edf5e2d 100644 --- a/classes/Custom_Validation.php +++ b/classes/Custom_Validation.php @@ -11,7 +11,7 @@ namespace TUTOR; /** - * Custom Valaidation Trait + * Custom Validation Trait * * @since 2.0.0 */ @@ -26,7 +26,7 @@ trait Custom_Validation { * @return bool */ public function validate_order( $order ) { - return in_array( strtolower( $order ), array( 'asc', 'desc' ) ); + return in_array( strtolower( $order ), array( 'asc', 'desc' ), true ); } } diff --git a/classes/Instructors_List.php b/classes/Instructors_List.php index 5769d45b21..1922829b91 100644 --- a/classes/Instructors_List.php +++ b/classes/Instructors_List.php @@ -10,9 +10,7 @@ namespace TUTOR; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} +defined( 'ABSPATH' ) || exit; use TUTOR\Students_List; use TUTOR\Backend_Page_Trait; @@ -129,9 +127,10 @@ public function tabs_key_value( $search = '', $course_id = '', $date = '' ): arr * Prepare bulk actions that will show on dropdown options * * @since 2.0.0 + * * @return array */ - public function prpare_bulk_actions(): array { + public function prepare_bulk_actions(): array { $actions = array( $this->bulk_action_default(), $this->bulk_action_approved(), diff --git a/classes/Lesson.php b/classes/Lesson.php index b11c417759..7c60edc675 100644 --- a/classes/Lesson.php +++ b/classes/Lesson.php @@ -148,9 +148,9 @@ public function ajax_single_course_lesson_load_more() { } if ( 'tutor_create_lesson_comment' === Input::post( 'action' ) && strlen( $comment ) > 0 ) { - $course_id = tutor_utils()->get_course_id_by( 'lesson', $lesson_id ); - if ( ! tutor_utils()->is_enrolled( $course_id ) ) { - wp_send_json_error( __( 'You must be enrolled to comment', 'tutor' ) ); + if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) { + wp_send_json_error( tutor_utils()->error_message() ); + return; } $comment_data = array( @@ -380,28 +380,18 @@ public function save_lesson_meta( $post_ID ) { */ public function ajax_lesson_details() { if ( ! tutor_utils()->is_nonce_verified() ) { - $this->json_response( tutor_utils()->error_message( 'nonce' ), null, HttpHelper::STATUS_BAD_REQUEST ); + $this->response_bad_request( tutor_utils()->error_message( 'nonce' ) ); } $topic_id = Input::post( 'topic_id', 0, Input::TYPE_INT ); $lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT ); - if ( ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) { - $this->json_response( - tutor_utils()->error_message(), - null, - HttpHelper::STATUS_FORBIDDEN - ); + if ( ! $topic_id || ! $lesson_id ) { + $this->response_bad_request( tutor_utils()->error_message( 'invalid_req' ) ); } - if ( 0 !== $lesson_id ) { - if ( ! tutor_utils()->can_user_manage( 'lesson', $lesson_id ) ) { - $this->json_response( - tutor_utils()->error_message(), - null, - HttpHelper::STATUS_FORBIDDEN - ); - } + if ( ! tutor_utils()->can_user_manage( 'topic', $topic_id ) || ! tutor_utils()->can_user_manage( 'lesson', $lesson_id ) ) { + $this->response_bad_request( tutor_utils()->error_message() ); } $data = LessonModel::get_lesson_details( $lesson_id ); @@ -423,7 +413,7 @@ public function ajax_lesson_details() { */ public function ajax_save_lesson() { if ( ! tutor_utils()->is_nonce_verified() ) { - $this->json_response( tutor_utils()->error_message( 'nonce' ), null, HttpHelper::STATUS_BAD_REQUEST ); + $this->response_bad_request( tutor_utils()->error_message( 'nonce' ) ); } /** @@ -434,22 +424,14 @@ public function ajax_save_lesson() { */ add_filter( 'wp_kses_allowed_html', Input::class . '::allow_iframe', 10, 2 ); - $is_update = false; - $lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT ); $topic_id = Input::post( 'topic_id', 0, Input::TYPE_INT ); - if ( $lesson_id ) { - $is_update = true; + if ( ! $topic_id || ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) { + $this->response_bad_request( tutor_utils()->error_message() ); } - if ( ! tutor_utils()->can_user_manage( 'topic', $topic_id ) ) { - $this->json_response( - tutor_utils()->error_message(), - null, - HttpHelper::STATUS_FORBIDDEN - ); - } + $is_update = $lesson_id > 0; $title = Input::post( 'title' ); $description = Input::post( 'description', '', Input::TYPE_KSES_POST ); @@ -540,13 +522,12 @@ public function ajax_delete_lesson() { tutor_utils()->check_nonce(); $lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT ); + if ( ! $lesson_id ) { + $this->response_bad_request( tutor_utils()->error_message( 'invalid_req' ) ); + } if ( ! tutor_utils()->can_user_manage( 'lesson', $lesson_id ) ) { - $this->json_response( - tutor_utils()->error_message(), - null, - HttpHelper::STATUS_FORBIDDEN - ); + $this->response_bad_request( tutor_utils()->error_message() ); } $content = __( 'Lesson', 'tutor' ); @@ -606,22 +587,29 @@ public function mark_lesson_complete() { if ( 'tutor_complete_lesson' !== Input::post( 'tutor_action' ) ) { return; } - // Checking nonce. + tutor_utils()->checking_nonce(); $user_id = get_current_user_id(); - // TODO: need to show view if not signed_in. if ( ! $user_id ) { die( esc_html__( 'Please Sign-In', 'tutor' ) ); } $lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT ); - if ( ! $lesson_id ) { return; } + $course_id = tutor_utils()->get_course_id_by( 'lesson', $lesson_id ); + if ( ! $course_id ) { + return; + } + + if ( ! tutor_utils()->is_enrolled( $course_id ) ) { + die( esc_html( tutor_utils()->error_message() ) ); + } + $validated = apply_filters( 'tutor_validate_lesson_complete', true, $user_id, $lesson_id ); if ( ! $validated ) { return; @@ -648,18 +636,19 @@ public function tutor_render_lesson_content() { tutor_utils()->checking_nonce(); $lesson_id = Input::post( 'lesson_id', 0, Input::TYPE_INT ); + if ( ! $lesson_id ) { + $this->response_bad_request( tutor_utils()->error_message( 'invalid_req' ) ); + } - $ancestors = get_post_ancestors( $lesson_id ); - $course_id = ! empty( $ancestors ) ? array_pop( $ancestors ) : $lesson_id; + $course_id = tutor_utils()->get_course_id_by( 'lesson', $lesson_id ); // Course must be public or current user must be enrolled to access this lesson. - if ( get_post_meta( $course_id, '_tutor_is_public_course', true ) !== 'yes' && ! EnrollmentModel::is_enrolled( $course_id ) ) { + if ( ! Course_List::is_public( $course_id ) && ! EnrollmentModel::is_enrolled( $course_id ) ) { - $is_admin = tutor_utils()->has_user_role( 'administrator' ); - $allowed = $is_admin ? true : tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id ); + $allowed = User::is_admin() ? true : tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id ); if ( ! $allowed ) { - http_response_code( 400 ); + $this->response_bad_request( tutor_utils()->error_message() ); exit; } } @@ -687,7 +676,12 @@ public function tutor_render_lesson_content() { public function autoload_next_course_content() { tutor_utils()->checking_nonce(); - $post_id = Input::post( 'post_id', 0, Input::TYPE_INT ); + $post_id = Input::post( 'post_id', 0, Input::TYPE_INT ); + $course_id = tutor_utils()->get_course_id_by_content( $post_id ); + if ( ! $course_id || ! EnrollmentModel::is_enrolled( $course_id ) ) { + wp_send_json_error( tutor_utils()->error_message() ); + } + $content_id = tutor_utils()->get_post_id( $post_id ); $contents = tutor_utils()->get_course_prev_next_contents_by_id( $content_id ); @@ -735,9 +729,8 @@ public function ajax_reply_lesson_comment() { } $lesson_id = Input::post( 'comment_post_ID', 0, Input::TYPE_INT ); - $course_id = tutor_utils()->get_course_id_by( 'lesson', $lesson_id ); - if ( ! tutor_utils()->is_enrolled( $course_id ) ) { - wp_send_json_error( __( 'You must be enrolled to comment', 'tutor' ) ); + if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $lesson_id ) ) { + wp_send_json_error( tutor_utils()->error_message() ); return; } diff --git a/classes/Options_V2.php b/classes/Options_V2.php index 035be259ea..9af44eae49 100644 --- a/classes/Options_V2.php +++ b/classes/Options_V2.php @@ -10,13 +10,12 @@ namespace Tutor; +defined( 'ABSPATH' ) || exit; + use TUTOR\Input; use Tutor\Traits\JsonResponse; use Tutor\Ecommerce\OptionKeys; - -if ( ! defined( 'ABSPATH' ) ) { - exit; -} +use TUTOR\User; /** * Contains all the settings options @@ -191,6 +190,10 @@ private function prepare_search_item( $section, $block, $field ) { public function tutor_option_search() { tutor_utils()->checking_nonce(); + if ( ! User::is_admin() ) { + wp_send_json_error( tutor_utils()->error_message() ); + } + $data_array = array(); foreach ( $this->get_setting_fields() as $sections ) { if ( is_array( $sections ) && ! empty( $sections ) ) { @@ -235,8 +238,8 @@ public function tutor_option_search() { */ public function tutor_export_settings() { tutor_utils()->checking_nonce(); - // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -278,7 +281,7 @@ public function tutor_export_single_settings() { tutor_utils()->checking_nonce(); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -308,7 +311,7 @@ public function tutor_apply_settings() { tutor_utils()->checking_nonce(); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -332,7 +335,7 @@ public function tutor_delete_single_settings() { tutor_utils()->checking_nonce(); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -357,35 +360,6 @@ public function get_request_data( $var ) { return isset( $_REQUEST[ $var ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $var ] ) ) : null; } - /** - * Tutor default settings update options - * and send json response - * - * @since 2.0.0 - * - * @return void send wp_json response - */ - public function tutor_default_settings() { - $attr = $this->get_setting_fields(); - - foreach ( $attr as $sections ) { - - foreach ( $sections as $section ) { - foreach ( $section['blocks'] as $blocks ) { - foreach ( $blocks['fields'] as $field ) { - if ( isset( $field['default'] ) ) { - $attr_default[ $field['key'] ] = $field['default']; - } - } - } - } - } - - update_option( 'tutor_option', $attr_default ); - - wp_send_json_success( $attr_default ); - } - /** * Tutor settings log * @@ -397,7 +371,7 @@ public function load_saved_data() { tutor_utils()->checking_nonce(); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -415,7 +389,7 @@ public function reset_settings_data() { tutor_utils()->checking_nonce(); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } @@ -452,10 +426,11 @@ public function tutor_import_settings() { tutor_utils()->checking_nonce(); // Check if user is privileged. - if ( ! current_user_can( 'administrator' ) ) { + if ( ! User::is_admin() ) { wp_send_json_error( tutor_utils()->error_message() ); } + //phpcs:ignore $data = $_FILES['data']; if ( ! isset( $data['tmp_name'] ) ) { @@ -541,7 +516,9 @@ public function validate_options( $options ) { public function tutor_option_save() { tutor_utils()->checking_nonce(); - ! current_user_can( 'manage_options' ) ? wp_send_json_error() : 0; + if ( ! User::is_admin() ) { + wp_send_json_error( tutor_utils()->error_message() ); + } $data_before = get_option( 'tutor_option' ); $login_page_id = 0; @@ -649,7 +626,10 @@ function ( $res ) { public function tutor_option_default_save() { tutor_utils()->checking_nonce(); - ! current_user_can( 'manage_options' ) ? wp_send_json_error() : 0; + if ( ! User::is_admin() ) { + wp_send_json_error( tutor_utils()->error_message() ); + } + $attr = $this->get_setting_fields(); $tutor_default_option = get_option( 'tutor_default_option' ); $tutor_saved_option = get_option( 'tutor_option' ); diff --git a/classes/Post_types.php b/classes/Post_types.php index 15e19681db..7fce36188b 100644 --- a/classes/Post_types.php +++ b/classes/Post_types.php @@ -119,6 +119,7 @@ public function register_course_post_types() { ), 'menu_icon' => 'dashicons-book-alt', 'capability_type' => 'post', + 'map_meta_cap' => true, 'has_archive' => true, 'hierarchical' => false, 'menu_position' => null, diff --git a/classes/User.php b/classes/User.php index 58cb609c3d..3b3d00bd76 100644 --- a/classes/User.php +++ b/classes/User.php @@ -145,6 +145,26 @@ public static function is( string $role ) { return current_user_can( $role ); } + /** + * Check current user has capability. + * + * Example usage: + * + * User::can( 'edit_posts' ); + * User::can( 'edit_post', $post->ID ); + * User::can( 'edit_post_meta', $post->ID, $meta_key ); + * + * @since 4.0.0 + * + * @param string $capability capability. + * @param mixed ...$args args. + * + * @return boolean + */ + public static function can( string $capability = 'manage_options', ...$args ) { + return current_user_can( $capability, ...$args ); + } + /** * Check user has any role. * @@ -165,7 +185,6 @@ public static function has_any_role( array $roles, $user_id = 0 ) { foreach ( $roles as $role ) { if ( in_array( $role, $user->roles, true ) ) { return true; - break; } } diff --git a/classes/Utils.php b/classes/Utils.php index 6667371e47..aac76ca20a 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -7330,14 +7330,14 @@ public function get_cover_photo_url( $user_id ) { } /** - * Return the course ID(s) by lession, quiz, answer etc. + * Return the course ID by lesson, quiz, answer etc. * * @since 1.7.9 * - * @param string $content content like lession, quiz, answer etc. + * @param string $content content like lesson, quiz, answer etc. * @param int $object_id object id. * - * @return int|int[] + * @return int */ public function get_course_id_by( $content, $object_id ) { $cache_key = "tutor_get_course_id_by_{$content}_{$object_id}"; diff --git a/templates/single/lesson/content.php b/templates/single/lesson/content.php index 43096e24f3..4b98680038 100644 --- a/templates/single/lesson/content.php +++ b/templates/single/lesson/content.php @@ -78,7 +78,7 @@ $json_data['lesson_completed'] = tutor_utils()->is_completed_lesson( $content_id, get_current_user_id() ) !== false; $json_data['is_enrolled'] = EnrollmentModel::is_enrolled( $course_id, get_current_user_id() ) !== false; ?> - +