芝麻web文件管理V1.00
编辑当前文件:/home/sditechnicalteam/socialdigivlms.com/wp-content/plugins/tutor/ecommerce/CouponController.php
* @link https://themeum.com * @since 3.0.0 */ namespace Tutor\Ecommerce; use TUTOR\Backend_Page_Trait; use TUTOR\BaseController; use TUTOR\Course; use Tutor\Helpers\DateTimeHelper; use Tutor\Helpers\HttpHelper; use Tutor\Helpers\ValidationHelper; use TUTOR\Input; use Tutor\Models\CouponModel; use Tutor\Models\CourseModel; use Tutor\Models\OrderModel; use Tutor\Traits\JsonResponse; use TutorPro\CourseBundle\Models\BundleModel; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * CouponController class * * @since 3.0.0 */ class CouponController extends BaseController { /** * Page slug * * @var string */ const PAGE_SLUG = 'tutor_coupons'; /** * Coupon model * * @since 3.0.0 * * @var CouponModel */ private $model; /** * Trait for utilities * * @var $page_title */ use Backend_Page_Trait; /** * Trait for sending JSON response */ use JsonResponse; /** * Page Title * * @var $page_title */ public $page_title; /** * Bulk Action * * @var $bulk_action */ public $bulk_action = true; /** * Constructor. * * Initializes the Coupons class, sets the page title, and optionally registers * hooks for handling AJAX requests related to coupon data, bulk actions, coupon status updates, * and coupon deletions. * * @param bool $register_hooks Whether to register hooks for handling requests. Default is true. * * @since 3.0.0 * * @return void */ public function __construct( $register_hooks = true ) { $this->page_title = __( 'Coupons', 'tutor' ); $this->model = new CouponModel(); if ( $register_hooks ) { // Register hooks here. add_action( 'wp_ajax_tutor_coupon_bulk_action', array( $this, 'bulk_action_handler' ) ); add_action( 'wp_ajax_tutor_coupon_permanent_delete', array( $this, 'coupon_permanent_delete' ) ); /** * Handle AJAX request for getting coupon related data by coupon ID. * * @since 3.0.0 */ add_action( 'wp_ajax_tutor_coupon_details', array( $this, 'ajax_coupon_details' ) ); /** * Handle AJAX request for getting courses for coupon. * * @since 3.0.0 */ add_action( 'wp_ajax_tutor_get_coupon_applies_to', array( $this, 'get_coupon_applies_to' ) ); add_action( 'wp_ajax_tutor_coupon_create', array( $this, 'ajax_create_coupon' ) ); add_action( 'wp_ajax_tutor_coupon_update', array( $this, 'ajax_update_coupon' ) ); add_action( 'wp_ajax_tutor_coupon_applies_to_list', array( $this, 'ajax_coupon_applies_to_list' ) ); add_action( 'wp_ajax_tutor_apply_coupon', array( $this, 'ajax_apply_coupon' ) ); } } /** * Get coupon model object * * @since 3.0.0 * * @return CouponModel */ public function get_model() { return $this->model; } /** * Handle ajax request for creating coupon * * @since 3.0.0 * * @return void send wp_json response */ public function ajax_create_coupon() { tutor_utils()->check_nonce(); tutor_utils()->check_current_user_capability(); $data = $this->get_allowed_fields( Input::sanitize_array( $_POST ), true );//phpcs:ignore --sanitized already if ( $this->model::TYPE_AUTOMATIC === $data['coupon_type'] ) { $data['coupon_code'] = time(); } $validation = $this->validate( $data ); if ( ! $validation->success ) { $this->json_response( tutor_utils()->error_message( 'validation_error' ), $validation->errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); } if ( $this->model->get_coupon( array( 'coupon_code' => $data['coupon_code'] ) ) ) { $this->json_response( __( 'Coupon code already exists!', 'tutor' ), null, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); } // Convert start & expire date time into gmt. $data['start_date_gmt'] = $data['start_date_gmt']; $data['created_by'] = get_current_user_id(); $data['created_at_gmt'] = current_time( 'mysql', true ); $data['updated_at_gmt'] = current_time( 'mysql', true ); $applies_to_items = isset( $data['applies_to_items'] ) ? $data['applies_to_items'] : array(); unset( $data['applies_to_items'] ); // Set expire date if isset. if ( isset( $data['expire_date_gmt'] ) ) { $data['expire_date_gmt'] = $data['expire_date_gmt']; } try { $coupon_id = $this->model->create_coupon( $data ); if ( $coupon_id ) { if ( is_array( $applies_to_items ) && count( $applies_to_items ) ) { $this->model->insert_applies_to( $data['applies_to'], $applies_to_items, $data['coupon_code'] ); } $this->json_response( __( 'Coupon created successfully!', 'tutor' ) ); } else { $this->json_response( __( 'Failed to create!', 'tutor' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR ); } } catch ( \Throwable $th ) { $this->json_response( tutor_utils()->error_message( 'server_error' ), $th->getMessage(), HttpHelper::STATUS_INTERNAL_SERVER_ERROR ); } } /** * Handle ajax request for updating coupon * * @since 3.0.0 * * @return void send wp_json response */ public function ajax_update_coupon() { tutor_utils()->check_nonce(); tutor_utils()->check_current_user_capability(); $data = $this->get_allowed_fields( Input::sanitize_array( $_POST ), false );//phpcs:ignore --sanitized already $coupon_id = Input::post( 'id', null, Input::TYPE_INT ); $data['coupon_id'] = $coupon_id; $data['updated_at_gmt'] = current_time( 'mysql', true ); $validation = $this->validate( $data ); if ( ! $validation->success ) { $this->json_response( tutor_utils()->error_message( 'validation_error' ), $validation->errors, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); } unset( $data['coupon_id'] ); if ( ! isset( $data['expire_date_gmt'] ) ) { $data['expire_date_gmt'] = null; } // Set updated by. $data['updated_by'] = get_current_user_id(); try { $update = $this->model->update_coupon( $coupon_id, $data ); if ( $update ) { $coupon_data = $this->model->get_coupon( array( 'id' => $coupon_id ) ); $this->model->delete_applies_to( $coupon_data->coupon_code ); if ( isset( $data['applies_to_items'] ) && is_array( $data['applies_to_items'] ) && count( $data['applies_to_items'] ) ) { $this->model->insert_applies_to( $data['applies_to'], $data['applies_to_items'], $coupon_data->coupon_code ); } $this->json_response( __( 'Coupon updated successfully!', 'tutor' ) ); } else { $this->json_response( __( 'Failed to update!', 'tutor' ), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR ); } } catch ( \Throwable $th ) { $this->json_response( tutor_utils()->error_message( 'server_error' ), $th->getMessage(), HttpHelper::STATUS_INTERNAL_SERVER_ERROR ); } } /** * Get list of coupon applies to on which coupon * will be applicable * * @since 3.0.0 * * @return void send wp_json response */ public function ajax_coupon_applies_to_list() { tutor_utils()->check_nonce(); tutor_utils()->check_current_user_capability(); $applies_to = Input::post( 'applies_to' ); $limit = Input::post( 'limit', 10, Input::TYPE_INT ); $offset = Input::post( 'offset', 0, Input::TYPE_INT ); $search_term = ''; $filter = json_decode( wp_unslash( $_POST['filter'] ) ); //phpcs:ignore --sanitized already if ( ! empty( $filter ) && property_exists( $filter, 'search' ) ) { $search_term = Input::sanitize( $filter->search ); } if ( $this->model->is_specific_applies_to( $applies_to ) ) { try { $list = $this->get_application_list( $applies_to, $limit, $offset, $search_term ); if ( $list ) { $this->json_response( __( 'Coupon application list retrieved successfully!', 'tutor' ), $list ); } else { $this->json_response( tutor_utils()->error_message( 'not_found' ), null, HttpHelper::STATUS_NOT_FOUND ); } } catch ( \Throwable $th ) { $this->json_response( tutor_utils()->error_message( 'server_error' ), $th->getMessage(), HttpHelper::STATUS_INTERNAL_SERVER_ERROR ); } } else { $this->json_response( tutor_utils()->error_message( 'invalid_req' ), null, HttpHelper::STATUS_UNPROCESSABLE_ENTITY ); } } /** * Prepare bulk actions that will show on dropdown options * * @return array * @since 3.0.0 */ public function prepare_bulk_actions(): array { $actions = array( $this->bulk_action_default(), $this->bulk_action_active(), $this->bulk_action_inactive(), ); $active_tab = Input::get( 'data', '' ); if ( 'trash' === $active_tab ) { array_push( $actions, $this->bulk_action_delete() ); } else { array_push( $actions, $this->bulk_action_trash() ); } return apply_filters( 'tutor_coupon_bulk_actions', $actions ); } /** * Get coupon page url * * @since 3.0.0 * * @param boolean $is_admin Whether to get admin or frontend url. * * @return string */ public static function get_coupon_page_url( bool $is_admin = true ) { if ( $is_admin ) { return admin_url( 'admin.php?page=' . self::PAGE_SLUG ); } else { return tutor_utils()->get_tutor_dashboard_url() . '/coupons'; } } /** * Available tabs that will visible on the right side of page navbar * * @return array * * @since 3.0.0 */ public function tabs_key_value(): array { $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link() ); $date = Input::get( 'date', '' ); $coupon_status = Input::get( 'coupon-status', '' ); $search = Input::get( 'search', '' ); $where = array(); if ( ! empty( $date ) ) { $where['created_at_gmt'] = tutor_get_formated_date( 'Y-m-d', $date ); } if ( ! empty( $coupon_status ) ) { $where['coupon_status'] = $coupon_status; } $coupon_status = $this->model->get_coupon_status(); $tabs = array(); $tabs [] = array( 'key' => 'all', 'title' => __( 'All', 'tutor' ), 'value' => $this->model->get_coupon_count( $where, $search ), 'url' => $url . '&data=all', ); foreach ( $coupon_status as $key => $value ) { $where['coupon_status'] = $key; $tabs[] = array( 'key' => $key, 'title' => $value, 'value' => $this->model->get_coupon_count( $where, $search ), 'url' => $url . '&data=' . $key, ); } return apply_filters( 'tutor_coupon_tabs', $tabs ); } /** * Get coupons * * @since 3.0.0 * * @param integer $limit List limit. * @param integer $offset List offset. * * @return array */ public function get_coupons( $limit = 10, $offset = 0 ) { $active_tab = Input::get( 'data', 'all' ); $date = Input::get( 'date', '' ); $search_term = Input::get( 'search', '' ); $coupon_status = Input::get( 'coupon-status' ); $where_clause = array(); if ( $date ) { $where_clause['created_at_gmt'] = tutor_get_formated_date( '', $date ); } if ( ! is_null( $coupon_status ) ) { $where_clause['coupon_status'] = $coupon_status; } if ( 'all' !== $active_tab && in_array( $active_tab, array_keys( $this->model->get_coupon_status() ), true ) ) { $where_clause['coupon_status'] = $active_tab; } $list_order = Input::get( 'order', 'DESC' ); $list_order_by = 'id'; return $this->model->get_coupons( $where_clause, $search_term, $limit, $offset, $list_order_by, $list_order ); } /** * Handle bulk action AJAX request. * * Bulk actions: active, inactive, trash, delete * * @since 3.0.0 * * @return void send wp_json response */ public function bulk_action_handler() { tutor_utils()->checking_nonce(); if ( ! current_user_can( 'manage_options' ) ) { tutor_utils()->error_message(); } // Get and sanitize input data. $request = Input::sanitize_array( $_POST ); //phpcs:ignore --sanitized already $bulk_action = $request['bulk-action']; $bulk_ids = isset( $request['bulk-ids'] ) ? array_map( 'intval', explode( ',', $request['bulk-ids'] ) ) : array(); if ( empty( $bulk_ids ) ) { wp_send_json_error( __( 'No items selected for the bulk action.', 'tutor' ) ); } $allowed_bulk_actions = array_keys( $this->model->get_coupon_status() ); array_push( $allowed_bulk_actions, 'delete' ); if ( ! in_array( $bulk_action, $allowed_bulk_actions, true ) ) { wp_send_json_error( __( 'Invalid bulk action.', 'tutor' ) ); } do_action( 'tutor_before_coupon_bulk_action', $bulk_action, $bulk_ids ); $response = false; if ( 'delete' === $bulk_action ) { $response = $this->model->delete_coupon( $bulk_ids ); } else { $data = array( 'coupon_status' => $bulk_action, ); $response = $this->model->update_coupon( $bulk_ids, $data ); } do_action( 'tutor_after_coupon_bulk_action', $bulk_action, $bulk_ids ); if ( $response ) { wp_send_json_success( __( 'Coupon updated successfully.', 'tutor' ) ); } else { wp_send_json_error( __( 'Failed to update coupon.', 'tutor' ) ); } } /** * Handle coupon permanent delete * * @since 3.0.0 * * @return void send wp_json response */ public function coupon_permanent_delete() { tutor_utils()->checking_nonce(); if ( ! current_user_can( 'manage_options' ) ) { tutor_utils()->error_message(); } // Get and sanitize input data. $id = Input::post( 'id', 0, Input::TYPE_INT ); if ( ! $id ) { wp_send_json_error( __( 'Invalid coupon ID', 'tutor' ) ); } do_action( 'tutor_before_coupon_permanent_delete', $id ); $response = $this->model->delete_coupon( $id ); if ( $response ) { do_action( 'tutor_after_coupon_permanent_delete', $id ); wp_send_json_success( __( 'Coupon delete successfully.', 'tutor' ) ); } else { wp_send_json_error( __( 'Failed to delete coupon.', 'tutor' ) ); } } /** * Ajax handler to retrieve coupon details. * * @since 3.0.0 * * @return void Sends a JSON response with the coupon data or an error message. */ public function ajax_coupon_details() { if ( ! tutor_utils()->is_nonce_verified() ) { $this->json_response( tutor_utils()->error_message( 'nonce' ), null, HttpHelper::STATUS_BAD_REQUEST ); } $coupon_id = Input::post( 'id' ); if ( empty( $coupon_id ) ) { $this->json_response( __( 'Coupon code is required', 'tutor' ), null, HttpHelper::STATUS_BAD_REQUEST ); } $coupon_data = $this->model->get_coupon( array( 'id' => $coupon_id ) ); if ( ! $coupon_data ) { $this->json_response( __( 'Coupon not found', 'tutor' ), null, HttpHelper::STATUS_NOT_FOUND ); } $applications = $this->model->get_formatted_coupon_applications( $coupon_data ); // Set applies to items. $coupon_data->applies_to_items = $applications; // Set coupon usage. $coupon_data->coupon_usage = $this->model->get_coupon_usage_count( $coupon_data->coupon_code ); // Set created & updated by. $coupon_data->coupon_created_by = tutor_utils()->display_name( $coupon_data->created_by ); $coupon_data->coupon_update_by = tutor_utils()->display_name( $coupon_data->updated_by ); $coupon_data->start_date_readable = empty( $coupon_data->start_date_gmt ) ? '' : DateTimeHelper::get_gmt_to_user_timezone_date( $coupon_data->start_date_gmt ); $coupon_data->expire_date_readable = empty( $coupon_data->expire_date_gmt ) ? '' : DateTimeHelper::get_gmt_to_user_timezone_date( $coupon_data->expire_date_gmt ); $coupon_data->created_at_readable = DateTimeHelper::get_gmt_to_user_timezone_date( $coupon_data->created_at_gmt ); $coupon_data->updated_at_readable = empty( $coupon_data->updated_at_gmt ) ? '' : DateTimeHelper::get_gmt_to_user_timezone_date( $coupon_data->updated_at_gmt ); $this->json_response( __( 'Coupon retrieved successfully', 'tutor' ), $coupon_data ); } /** * Get application if applies to a specific category or bundle. * * @since 3.0.0 * * @param string $applies_to Applies to. * @param int $limit Number of items to fetch. * @param int $offset Offset for fetching items. * @param int $search_term Search term. * * @return array */ public function get_application_list( string $applies_to, int $limit = 10, int $offset = 0, $search_term = '' ) { $response = array( 'total_items' => 0, 'results' => array(), ); if ( $this->model::APPLIES_TO_SPECIFIC_COURSES === $applies_to ) { $args = array( 'post_type' => tutor()->course_post_type, 'posts_per_page' => $limit, 'offset' => $offset, ); // Add search. if ( $search_term ) { $args['s'] = $search_term; } $courses = ( new CourseModel() )->get_paid_courses( $args ); $response['total_items'] = is_a( $courses, 'WP_Query' ) ? $courses->found_posts : 0; if ( is_a( $courses, 'WP_Query' ) && $courses->have_posts() ) { $courses = $courses->get_posts(); foreach ( $courses as $course ) { $response['results'][] = Course::get_mini_info( $course ); } } } elseif ( $this->model::APPLIES_TO_SPECIFIC_BUNDLES === $applies_to && tutor_utils()->is_addon_enabled( 'tutor-pro/addons/course-bundle/course-bundle.php' ) ) { $args = array( 'post_type' => 'course-bundle', 'posts_per_page' => $limit, 'offset' => $offset, ); // Add search. if ( $search_term ) { $args['s'] = $search_term; } $bundles = ( new CourseModel() )->get_paid_courses( $args ); $response['total_items'] = is_a( $bundles, 'WP_Query' ) ? $bundles->found_posts : 0; if ( is_a( $bundles, 'WP_Query' ) && $bundles->have_posts() ) { $bundles = $bundles->get_posts(); foreach ( $bundles as $bundle ) { $response['results'][] = Course::get_mini_info( $bundle ); } } } elseif ( $this->model::APPLIES_TO_SPECIFIC_CATEGORY === $applies_to ) { $args = array( 'number' => $limit, 'offset' => $offset, 'hide_empty' => true, ); $total_arg = array( 'fields' => 'ids', 'taxonomy' => CourseModel::COURSE_CATEGORY, 'hide_empty' => true, ); // Add search. if ( $search_term ) { $args['search'] = $search_term; $total_arg['search'] = $search_term; } $terms = tutor_utils()->get_course_categories( 0, $args ); $total = get_terms( $total_arg ); $response['total_items'] = is_array( $total ) ? count( $total ) : 0; if ( ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $thumb_id = get_term_meta( $term->term_id, 'thumbnail_id', true ); $response['results'][] = array( 'id' => $term->term_id, 'title' => $term->name, 'image' => $thumb_id ? wp_get_attachment_thumb_url( $thumb_id ) : tutor()->url . 'assets/images/placeholder.svg', 'total_courses' => (int) $term->count, ); } } } return $response; } /** * Ajax handler for applying coupon * * @since 3.0.0 * * @return void send wp_json response */ public function ajax_apply_coupon() { tutor_utils()->check_nonce(); if ( ! Settings::is_coupon_usage_enabled() ) { $this->json_response( __( 'Coupon usage is disabled', 'tutor' ), null, HttpHelper::STATUS_BAD_REQUEST ); } $object_ids = Input::post( 'object_ids' ); // Course/bundle ids. $object_ids = array_filter( explode( ',', $object_ids ), 'is_numeric' ); if ( empty( $object_ids ) ) { $this->json_response( tutor_utils()->error_message( 'invalid_req' ), null, HttpHelper::STATUS_BAD_REQUEST ); } try { $coupon_code = Input::post( 'coupon_code' ); $plan = Input::post( 'plan', 0, Input::TYPE_INT ); $order_type = $plan ? OrderModel::TYPE_SUBSCRIPTION : OrderModel::TYPE_SINGLE_ORDER; $checkout_data = ( new CheckoutController( false ) )->prepare_checkout_items( $object_ids, $order_type, $coupon_code ); if ( $checkout_data->is_coupon_applied ) { $this->json_response( __( 'Coupon applied successfully', 'tutor' ), $checkout_data ); } else { $this->json_response( __( 'Coupon code is not applicable!', 'tutor' ), null, HttpHelper::STATUS_BAD_REQUEST ); } } catch ( \Throwable $th ) { $this->json_response( $th->getMessage(), null, HttpHelper::STATUS_INTERNAL_SERVER_ERROR ); } } /** * Manage coupon usage * * Store usage upon order completion * * @since 3.0.0 * * @param int $order_id Order id. * * @return void */ public function store_coupon_usage( $order_id ) { $order_model = ( new OrderModel() ); $order = $order_model->get_order_by_id( $order_id ); if ( $order ) { if ( $order->coupon_amount > 0 && $order_model::ORDER_COMPLETED === $order->order_status ) { // Store coupon usage. $data = array( 'coupon_code' => $order->coupon_code, 'user_id' => $order->user_id, ); try { $this->model->store_coupon_usage( $data ); } catch ( \Throwable $th ) { tutor_log( $th ); } } } } /** * Validate input data based on predefined rules. * * @since 3.0.0 * * @param array $data The data array to validate. * * @return object The validation result. It returns validation object. */ protected function validate( array $data ) { $validation_rules = array( 'coupon_id' => 'numeric', 'coupon_status' => 'required', 'coupon_type' => 'required', 'coupon_code' => 'required', 'coupon_title' => 'required', 'discount_type' => 'required', 'discount_amount' => 'required', 'applies_to' => 'required', 'total_usage_limit' => 'numeric', 'per_user_usage_limit' => 'numeric', 'start_date_gmt' => 'required|date_format:Y-m-d H:i:s', ); // Skip validation rules for not available fields in data. foreach ( $validation_rules as $key => $value ) { if ( ! array_key_exists( $key, $data ) ) { unset( $validation_rules[ $key ] ); } } return ValidationHelper::validate( $validation_rules, $data ); } }