%PDF- %PDF-
Direktori : /var/www/html/itworks/wp-content/plugins/learnpress/inc/ |
Current File : /var/www/html/itworks/wp-content/plugins/learnpress/inc/class-lp-repair-database.php |
<?php /** * Class LP_Repair_Database * * Repair database tool * * @since 3.1.0 */ class LP_Repair_Database { /** * @var LP_Repair_Database */ protected static $instance = null; /** * Posts are being deleted. * * @var array */ protected $deleting_posts = array(); /** * LP_Repair_Database constructor. * * @access protected */ protected function __construct() { add_action( 'save_post', array( $this, 'save_post' ), 0 ); //add_action( 'deleted_post', array( $this, 'save_post' ), 0 ); //add_action( 'learn-press/added-item-to-section', array( $this, 'added_item_to_section' ), 5000, 3 ); //add_action( 'learn-press/removed-item-from-section', array( $this, 'removed_item_from_course' ), 5000, 2 ); add_action( 'learn-press/save-course', array( $this, 'save_course' ), 5000, 1 ); add_action( 'learn-press/added-course-item', array( $this, 'added_course_item' ), 10, 2 ); add_action( 'learn-press/removed-course-item', array( $this, 'removed_course_item' ), 10, 2 ); add_action( 'learn-press/transition-course-item-status', array( $this, 'transition_course_item_status' ), 10, 4 ); add_action( 'before_delete_post', array( $this, 'before_delete_post' ) ); add_action( 'deleted_post', array( $this, 'deleted_post' ) ); //add_action( 'save_post', array( $this, 'save_post' ) ); } /** * Stores some information of the post is being deleted. * Eg post-type will be lost after deleted post. * * @since 3.1.0 * * @param int $post_id */ public function before_delete_post( $post_id ) { global $wpdb; //LP_Debug::startTransaction(); $post_type = get_post_type( $post_id ); $data = array( 'post_type' => $post_type ); switch ( $post_type ) { case LP_ORDER_CPT: $order = learn_press_get_order( $post_id ); $data['users'] = $order->get_users(); $data['child'] = $order->get_child_orders(); break; } $this->deleting_posts[ $post_id ] = $data; } /** * Do stuff after post deleted. * * @since 3.1.0 * * @param int $post_id */ public function deleted_post( $post_id ) { try { if ( ! empty( $this->deleting_posts[ $post_id ] ) ) { $data = $this->deleting_posts[ $post_id ]; $post_type = ! empty( $data['post_type'] ) ? $data['post_type'] : ''; switch ( $post_type ) { case LP_ORDER_CPT: if ( ! empty( $data['users'] ) ) { $this->remove_order_from_user_meta( $data['users'], $post_id ); } $this->remove_order_items( $post_id ); $this->remove_user_items_by_order_id( $post_id ); if ( ! empty( $data['child'] ) ) { $this->remove_child_orders( $data['child'] ); } break; } } } catch ( Exception $ex ) { echo $ex->getMessage(); } //LP_Debug::rollbackTransaction(); //die(); } /** * Remove order items and it meta data from tables * learnpress_user_items and learnpress_user_itemmeta * by order id. * * @since 3.1.0 * * @param int $order_id * * @return bool|int[] */ public function remove_order_items( $order_id ) { global $wpdb; if ( is_array( $order_id ) ) { foreach ( $order_id as $oid ) { $this->remove_order_items( $oid ); } return true; } $order_item_id = $wpdb->get_col( $wpdb->prepare( "SELECT order_item_id FROM {$wpdb->learnpress_order_items} WHERE order_id = %d", $order_id ) ); if ( ! $order_item_id ) { return false; } $format = array_fill( 0, sizeof( $order_item_id ), '%d' ); // Delete rows from order-items and order-itemmeta $query = $wpdb->prepare( " DELETE FROM {$wpdb->learnpress_order_itemmeta} WHERE learnpress_order_item_id IN(" . join( ',', $format ) . ") ", $order_item_id ); $wpdb->query( $query ); $query = $wpdb->prepare( " DELETE FROM {$wpdb->learnpress_order_items} WHERE order_item_id IN(" . join( ',', $format ) . ") ", $order_item_id ); $wpdb->query( $query ); return $order_item_id; } /** * @param int[] $order_ids */ public function remove_child_orders( $order_ids ) { global $wpdb; foreach ( $order_ids as $order_id ) { wp_delete_post( $order_id ); } } /** * Remove user items from table learnpress_user_items * and it's meta data from table learnpress_user_itemmeta * by order id. Uses this function when we want to remove * a course-item and all child-items reference to a an order. * * @since 3.1.0 * * @param int[] $order_id * * @return bool */ public function remove_user_items_by_order_id( $order_id ) { if ( is_array( $order_id ) ) { foreach ( $order_id as $oid ) { $this->remove_user_items_by_order_id( $oid ); } return true; } global $wpdb; $query = $wpdb->prepare( " SELECT user_item_id FROM {$wpdb->learnpress_user_items} WHERE ref_id = %d AND ref_type = %s ", $order_id, LP_ORDER_CPT ); if ( ! $user_item_ids = $wpdb->get_col( $query ) ) { return false; } $this->remove_user_items_by_user_item_id( $user_item_ids, true ); return true; } /** * Remove user item from table user-items by primary key user_item_id. * This function also remove row's meta data and/or child rows. * * @since 3.1.0 * * @param int[] $user_item_id * @param bool $remove_child - Optional. TRUE will remove it's child */ public function remove_user_items_by_user_item_id( $user_item_id, $remove_child = true ) { global $wpdb; settype( $user_item_id, 'array' ); $format = array_fill( 0, sizeof( $user_item_id ), '%d' ); // Remove child if ( $remove_child && $child_user_item_ids = $this->get_user_items_by_parent_id( $user_item_id ) ) { $this->remove_user_items_by_user_item_id( $child_user_item_ids, $remove_child ); } $query = $wpdb->prepare( " DELETE FROM {$wpdb->learnpress_user_itemmeta} WHERE learnpress_user_item_id IN(" . join( ',', $format ) . ") ", $user_item_id ); $wpdb->query( $query ); $query = $wpdb->prepare( " DELETE FROM {$wpdb->learnpress_user_items} WHERE user_item_id IN(" . join( ',', $format ) . ") ", $user_item_id ); $wpdb->query( $query ); } /** * Remove user item from table user-items by specific user-id and item-id. * * @since 3.1.0 * * @param int $user_id * @param int $item_id * * @return array|bool */ public function remove_user_item( $user_id, $item_id ) { global $wpdb; // Find user-item-id for passed user-id and item-id $query = $wpdb->prepare( " SELECT user_item_id FROM {$wpdb->learnpress_user_items} WHERE user_id = %d AND item_id = %d ", $user_id, $item_id ); if ( $user_item_ids = $wpdb->get_col( $query ) ) { return false; } // Remove by user-item-ids $this->remove_user_items_by_user_item_id( $user_item_ids ); return $user_item_ids; } /** * Remove user item from table user-items by user-id. * Uses this function in case we want to remove all item * from an user (such as when an user will be deleted). * * @since 3.1.0 * * @param int[] $user_id * * @return bool */ public function remove_user_item_by_user( $user_id ) { global $wpdb; // If multi users passed, loop through each user if ( is_array( $user_id ) ) { foreach ( $user_id as $uid ) { $this->remove_user_item_by_user( $uid ); } return true; } $query = $wpdb->prepare( " SELECT user_item_id FROM {$wpdb->learnpress_user_items} WHERE user_id = %d ", $user_id ); if ( ! $user_item_ids = $wpdb->get_col( $query ) ) { return false; } $this->remove_user_items_by_user_item_id( $user_item_ids ); return true; } /** * Remove user item from table user-items by item-id. * Uses this function in case want to remove an item * from all users (such as an item like course/lesson/quiz will be deleted). * * @since 3.1.0 * * @param int $item_id * * @return array|bool */ public function remove_user_item_by_item_id( $item_id ) { global $wpdb; $query = $wpdb->prepare( " SELECT user_item_id FROM {$wpdb->learnpress_user_items} WHERE item_id = %d ", $item_id ); if ( ! $user_item_ids = $wpdb->get_col( $query ) ) { return false; } $this->remove_user_items_by_user_item_id( $user_item_ids ); return $user_item_ids; } /** * Retrieve child items from an user-item-id. * * @since 3.1.0 * * @param int[] $parent_ids * * @return array */ public function get_user_items_by_parent_id( $parent_ids ) { global $wpdb; settype( $parent_ids, 'array' ); $format = array_fill( 0, sizeof( $parent_ids ), '%d' ); $query = $wpdb->prepare( " SELECT user_item_id FROM {$wpdb->learnpress_user_items} WHERE parent_id IN(" . join( ',', $format ) . ") ", $parent_ids ); return $wpdb->get_col( $query ); } /** * Remove an order from user-meta. * * @since 3.1.0 * * @param int[] $user_ids * @param int $order_id */ public function remove_order_from_user_meta( $user_ids, $order_id ) { if ( ! $user_ids ) { return; } settype( $user_ids, 'array' ); foreach ( $user_ids as $user_id ) { $user_orders = get_user_meta( $user_id, 'orders', true ); if ( $user_orders ) { foreach ( $user_orders as $course_id => $course_orders ) { $course_orders = array_unique( $course_orders ); if ( false !== ( $in_pos = array_search( $order_id, $course_orders ) ) ) { unset( $course_orders[ $in_pos ] ); } if ( ! $course_orders ) { unset( $user_orders[ $course_id ] ); } else { $user_orders[ $course_id ] = $course_orders; } } } update_user_meta( $user_id, 'order', $user_orders ); } } public function add_order_to_user_meta( $order_id ) { if ( ! $order = learn_press_get_order( $order_id ) ) { return false; } if ( $order->is_multi_users() ) { if ( ! $child = $order->get_child_orders() ) { return false; } foreach ( $child as $child_order_id ) { $child_order = learn_press_get_order( $child_order_id ); } return true; } $user_id = $order->get_user_id(); $user_orders = get_user_meta( $user_id, 'orders', true ); if ( $user_orders ) { foreach ( $user_orders as $course_id => $course_orders ) { $course_orders = array_unique( $course_orders ); if ( false !== ( $in_pos = array_search( $order_id, $course_orders ) ) ) { unset( $course_orders[ $in_pos ] ); } if ( ! $course_orders ) { unset( $user_orders[ $course_id ] ); } else { $user_orders[ $course_id ] = $course_orders; } } } return true; } public function save_course( $course_id ) { $this->sync_course_data( $course_id ); } public function removed_course_item( $item_id, $course_id ) { $this->sync_course_data( $course_id ); //$this->remove_user_item( $item_id ); } /** * @param int $item_id * @param int $course_id */ public function added_course_item( $item_id, $course_id ) { $this->sync_course_data( $course_id ); } /** * @param int $item_id * @param int $course_id */ public function removed_item_from_course( $item_id, $course_id ) { $this->sync_course_data( $course_id ); } public function transition_course_item_status( $item_id, $course_id, $old, $new ) { if ( $old === $new ) { return; } $this->sync_course_data( $course_id ); } public function get_user_item_type( $item_id ) { global $wpdb; if ( ! $item_type = get_post_type( $item_id ) ) { $query = $wpdb->prepare( " SELECT item_type FROM {$wpdb->learnpress_user_items} WHERE item_id = %d LIMIT 0,1 ", $item_id ); $item_type = $wpdb->get_var( $query ); } return $item_type; } public function remove_user_itemx( $item_id ) { global $wpdb; $query = " DELETE items, meta FROM {$wpdb->learnpress_user_items} items INNER JOIN {$wpdb->learnpress_user_itemmeta} meta ON items.user_item_id = meta.learnpress_user_item_id "; $where = ""; if ( $this->get_user_item_type( $item_id ) === LP_COURSE_CPT ) { $_query = $wpdb->prepare( " SELECT user_item_id FROM {$wpdb->learnpress_user_items} WHERE item_id = %d AND parent_id = 0 ", $item_id ); $user_item_ids = $wpdb->get_col( $_query ); $format = array_fill( 0, sizeof( $user_item_ids ), '%d' ); $where = $wpdb->prepare( " WHERE parent_id IN(" . join( ',', $format ) . ") ", $user_item_ids ); $where .= $wpdb->prepare( "AND ref_id = %d", $item_id ); } else { $where = $wpdb->prepare( "item_id = %d", $item_id ); } $query .= $where; } /** * @param int $post_id */ public function save_post( $post_id ) { global $wpdb; $post_type = get_post_type( $post_id ); $course_curd = new LP_Course_CURD(); switch ( $post_type ) { case LP_ORDER_CPT: if ( $order = learn_press_get_order( $post_id ) ) { $user_ids = $order->get_users(); $course_ids = $order->get_item_ids(); $this->sync_course_orders( $course_ids ); $this->sync_user_orders( $user_ids ); } break; case LP_COURSE_CPT: default: // Course is support type of this item? if ( learn_press_is_support_course_item_type( $post_type ) ) { // Find it course $course_ids = $course_curd->get_course_by_item( $post_id ); } else { $course_ids = array( $post_id ); } foreach ( $course_ids as $course_id ) { $this->sync_course_data( $course_id ); } } } /** * Sync course data when saving post. * * @since 3.1.0 * * @param int $course_id */ public function sync_course_data( $course_id ) { $user_curd = new LP_User_CURD(); $course_curd = new LP_Course_CURD(); $count_items = 0; if ( $counts = $course_curd->count_items( $course_id ) ) { $count_items = array_sum( $counts ); } update_post_meta( $course_id, 'count_items', $count_items ); $this->queue_sync_user_course_results( $course_id ); } /** * Sync all */ public function sync_all() { $this->sync_course_orders(); $this->sync_user_courses(); } public function call( $func ) { $func = preg_replace( '~[-]+~', '_', $func ); if ( ! is_callable( array( $this, $func ) ) ) { throw new Exception( sprintf( __( 'The method %s is not callable.', 'learnpress' ), $func ) ); } $args = func_get_args(); unset( $args[0] ); return sizeof( $args ) ? call_user_func_array( array( $this, $func ), $args ) : call_user_func( array( $this, $func ) ); } public function queue_sync_user_course_results( $course_id ) { global $wpdb; $query = $wpdb->prepare( " SELECT DISTINCT user_id FROM {$wpdb->learnpress_user_items} WHERE item_id = %d ", $course_id ); if ( $user_ids = $wpdb->get_col( $query ) ) { $queue_user_ids = get_option( 'sync-user-course-results' ); $first_time = ! $queue_user_ids; $n = time(); for ( $i = $n; $i < $n + 100; $i ++ ) { $user_ids[] = $i; } if ( $first_time ) { $queue_user_ids = $user_ids; } else { settype( $queue_user_ids, 'array' ); $queue_user_ids = array_merge( $queue_user_ids, $user_ids ); $queue_user_ids = array_unique( $queue_user_ids ); } $option_key = 'sync-user-course-results'; update_option( $option_key, $queue_user_ids, 'no' ); if ( $first_time || ! get_option( 'doing-sync-user-course-results' ) ) { $bg = LP_Background_Sync_Data::instance(); $bg->is_safe( false ); $bg->push_to_queue( array( 'action' => 'sync-user-course-results', 'course_id' => $course_id, 'option_key' => $option_key ) )->save()->dispatch(); $bg->reset_safe(); update_option( 'doing-sync-user-course-results', 'yes' ); } } } /** * Sync orders for each course * * @since 3.1.0 * * @param array|string $courses * * @return bool|array */ public function sync_course_orders( $courses = '*' ) { global $wpdb; if ( empty( $courses ) ) { return false; } if ( $courses === '*' ) { $query = $wpdb->prepare( " SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s ", LP_COURSE_CPT, 'publish' ); $courses = $wpdb->get_col( $query ); if ( $courses ) { return false; } } $statuses = learn_press_get_order_statuses( true, true ); settype( $courses, 'array' ); $statuses_format = array_fill( 0, sizeof( $statuses ), '%s' ); $courses_format = array_fill( 0, sizeof( $courses ), '%d' ); $statuses_format = $wpdb->prepare( join( ',', $statuses_format ), $statuses ); $courses_format = $wpdb->prepare( join( ',', $courses_format ), $courses ); $wpdb->query( 'SET SESSION group_concat_max_len = 18446744073709551615' ); $query = $wpdb->prepare( " SELECT cid, status, orders FROM( SELECT oim.meta_value cid, concat(oim.meta_value, ' ', o.post_status) a, post_status `status`, GROUP_CONCAT(o.ID) orders FROM {$wpdb->learnpress_order_itemmeta} oim INNER JOIN {$wpdb->learnpress_order_items} oi ON oi.order_item_id = oim.learnpress_order_item_id AND oim.meta_key = %s INNER JOIN {$wpdb->posts} o ON o.ID = oi.order_id INNER JOIN {$wpdb->postmeta} om ON o.ID = om.post_id AND `om`.`meta_key`='_user_id' INNER JOIN {$wpdb->users} `u` ON u.ID = `om`.`meta_value` WHERE o.post_type = %s AND o.post_status IN ($statuses_format) AND oim.meta_value IN ($courses_format) GROUP BY a, cid ) X ", '_course_id', 'lp_order' ); foreach ( $courses as $course_id ) { foreach ( $statuses as $status ) { update_post_meta( $course_id, 'order-' . str_replace( 'lp-', '', $status ), array() ); } } if ( $results = $wpdb->get_results( $query ) ) { foreach ( $results as $result ) { update_post_meta( $result->cid, 'order-' . str_replace( 'lp-', '', $result->status ), explode( ',', $result->orders ) ); } } return $courses; } /** * Sync orders for each user * * @param array $users */ public function sync_user_orders( $users = array() ) { $api = new LP_User_CURD(); settype( $users, 'array' ); foreach ( $users as $user ) { if ( ! $orders = $api->read_orders( $user ) ) { continue; } $orders = array_map( 'array_unique', $orders ); update_user_meta( $user, 'orders', $orders ); } } /** * Sync courses for each user * * @since 3.1.0 */ public function sync_user_courses() { //echo __FUNCTION__; } /** * Sync final quiz for each course. * * @param array $courses */ public function sync_course_final_quiz( $courses = array() ) { settype( $courses, 'array' ); foreach ( $courses as $course_id ) { if ( ! $course = learn_press_get_course( $course_id ) ) { continue; } /** * If course result is not set to final-quiz */ if ( $course->get_data( 'course_result' ) !== 'evaluate_final_quiz' ) { delete_post_meta( $course_id, '_lp_final_quiz' ); continue; } $items = $course->get_item_ids(); if ( $items ) { $end = end( $items ); if ( learn_press_get_post_type( $end ) === LP_QUIZ_CPT ) { $final_quiz = $end; } } if ( isset( $final_quiz ) ) { update_post_meta( $course_id, '_lp_final_quiz', $final_quiz ); } else { delete_post_meta( $course_id, '_lp_final_quiz' ); update_post_meta( $course_id, '_lp_course_result', 'evaluate_lesson' ); } } } public function calculate_course_results( $user_id ) { settype( $user_id, 'array' ); global $wpdb; foreach ( $user_id as $uid ) { $query = $wpdb->prepare( " SELECT DISTINCT item_id FROM {$wpdb->learnpress_user_items} ui INNER JOIN {$wpdb->posts} p ON p.ID = ui.item_id WHERE user_id = %d AND p.post_type = %s ", $uid, LP_COURSE_CPT ); if ( $course_ids = $wpdb->get_col( $query ) ) { $user = learn_press_get_user( $uid ); foreach ( $course_ids as $course_id ) { $item_course = $user->get_course_data( $course_id ); $course_exceeded = $item_course->is_exceeded(); if ( $course_exceeded <= 0 && ( $item_course->get_status() === 'enrolled' ) ) { $item_course->finish(); $start_time = $item_course->get_start_time()->getTimestamp(); $duration = $item_course->get_course()->get_duration(); learn_press_update_user_item_meta( $item_course->get_user_item_id(), 'via', 'schedule' ); learn_press_update_user_item_meta( $item_course->get_user_item_id(), 'exceeded', $course_exceeded ); } else { $item_course->calculate_course_results(); } } } } } public function remove_older_post_meta() { global $wpdb; $query = $wpdb->prepare( " DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s ", $wpdb->esc_like( '_lpr_' ) . '%' ); $wpdb->query( $query ); $query = $wpdb->prepare( " DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s ", '%' . $wpdb->esc_like( '_lpr_' ) . '%' ); $wpdb->query( $query ); } /** * Get all ids of existing courses * * @return array */ public function get_all_courses() { global $wpdb; $query = $wpdb->prepare( " SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s ", LP_COURSE_CPT, 'publish' ); return $wpdb->get_col( $query ); } /** * @return LP_Repair_Database */ public static function instance() { if ( ! self::$instance ) { self::$instance = new self(); } return self::$instance; } } LP_Repair_Database::instance();