PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
← All changes | inc/custom-post-types/course.php +309 -320 4.2.04.4.8 View file →
@@ -3,11 +3,18 @@
3 3 * Class LP_Course_Post_Type
4 4 *
5 5 * @author ThimPress
6 6 * @package LearnPress/Classes
7 - * @version 3.0.0
7 + * @version 3.0.3
8 8 */
9 9
10 +use LearnPress\Databases\PostDB;
11 +use LearnPress\Filters\CoursePostFilter;
12 +use LearnPress\Models\CourseModel;
13 +use LearnPress\Models\CoursePostModel;
14 +use LearnPress\Models\PostModel;
15 +use LearnPress\Models\WPTables\CoursesTable;
16 +
10 17 defined( 'ABSPATH' ) || exit();
11 18
12 19 if ( ! class_exists( 'LP_Course_Post_Type' ) ) {
13 20
@@ -18,15 +25,12 @@
18 25 /**
19 26 * @var null
20 27 */
21 28 protected static $_instance = null;
29 + protected $_post_type = LP_COURSE_CPT;
30 + protected $_screen_list = 'edit-' . LP_COURSE_CPT;
22 31
23 32 /**
24 - * @var string
25 - */
26 - protected $_post_type = LP_COURSE_CPT;
27 -
28 - /**
29 33 * Constructor
30 34 */
31 35 public function __construct() {
32 36 parent::__construct();
@@ -31,76 +35,20 @@
31 35 public function __construct() {
32 36 parent::__construct();
33 37
34 38 add_action( 'init', array( $this, 'register_taxonomy' ) );
35 - add_filter( 'posts_where_paged', array( $this, '_posts_where_paged_course_items' ), 10 );
36 - add_filter( 'posts_join_paged', array( $this, '_posts_join_paged_course_items' ), 10 );
37 -
38 - // Comment by tungnx
39 - // add_action( 'learn-press/admin/after-enqueue-scripts', array( $this, 'data_course_editor' ) );
40 - add_action( 'admin_enqueue_scripts', array( $this, 'add_script_data' ) );
39 + add_action( 'posts_pre_query', array( $this, 'posts_pre_query' ), 999, 2 );
40 + // add_filter( 'posts_where_paged', array( $this, '_posts_where_paged_course_items' ), 10 );
41 + // add_filter( 'posts_join_paged', array( $this, '_posts_join_paged_course_items' ), 10 );
41 42 }
42 43
43 - public function add_script_data() {
44 - global $post, $pagenow;
45 -
46 - if ( empty( $post ) || ( get_post_type() !== $this->_post_type ) || ! in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) ) {
47 - return;
48 - }
49 -
50 - $course = learn_press_get_course( $post->ID );
51 - $hidden_sections = get_post_meta( $post->ID, '_admin_hidden_sections', true );
52 -
53 - $data = apply_filters(
54 - 'learn-press/admin-localize-course-editor',
55 - array(
56 - 'root' => array(
57 - 'course_id' => $post->ID,
58 - 'auto_draft' => get_post_status( $post->ID ) == 'auto-draft',
59 - 'ajax' => admin_url( 'index.php' ),
60 - 'disable_curriculum' => false,
61 - 'action' => 'admin_course_editor',
62 - 'nonce' => wp_create_nonce( 'learnpress_update_curriculum' ),
63 - ),
64 - 'chooseItems' => array(
65 - 'types' => learn_press_course_get_support_item_types(),
66 - 'open' => false,
67 - 'addedItems' => array(),
68 - 'items' => array(),
69 - ),
70 - 'i18n' => array(
71 - 'item' => __( 'item', 'learnpress' ),
72 - 'new_section_item' => __( 'Create a new', 'learnpress' ),
73 - 'back' => __( 'Back', 'learnpress' ),
74 - 'selected_items' => __( 'Selected items', 'learnpress' ),
75 - 'confirm_remove_item' => __( 'Do you want to remove the "{{ITEM_NAME}}" item from the course?', 'learnpress' ),
76 - 'confirm_trash_item' => __( 'Do you want to move the "{{ITEM_NAME}}" item to the trash?', 'learnpress' ),
77 - 'item_labels' => array(
78 - 'singular' => __( 'Item', 'learnpress' ),
79 - 'plural' => __( 'Items', 'learnpress' ),
80 - ),
81 - 'notice_sale_price' => __( 'The course sale price must be less than the regular price', 'learnpress' ),
82 - 'notice_price' => __( 'The course price must be greater than the sale price', 'learnpress' ),
83 - 'notice_sale_start_date' => __( 'The sale start date must be before the sale end date', 'learnpress' ),
84 - 'notice_sale_end_date' => __( 'The sale end date must be after the sale start date', 'learnpress' ),
85 - 'notice_invalid_date' => __( 'Invalid date', 'learnpress' ),
86 - ),
87 - 'sections' => array(
88 - 'sections' => $course->get_curriculum_raw(),
89 - 'hidden_sections' => ! empty( $hidden_sections ) ? $hidden_sections : array(),
90 - 'urlEdit' => admin_url( 'post.php?action=edit&post=' ),
91 - ),
92 - )
93 - );
94 -
95 - learn_press_admin_assets()->add_script_data( 'learn-press-admin-course-editor', $data );
96 - }
97 -
98 44 /**
99 45 * Register course post type.
100 46 */
101 - public function args_register_post_type() : array {
102 - $settings = LP_Settings::instance();
47 + public function args_register_post_type(): array {
48 + // Support Quick Edit multiple courses change author
49 + add_post_type_support( LP_COURSE_CPT, 'author' );
50 +
103 51 $labels = array(
104 52 'name' => _x( 'Courses', 'Post Type General Name', 'learnpress' ),
105 53 'singular_name' => _x( 'Course', 'Post Type Singular Name', 'learnpress' ),
106 54 'menu_name' => __( 'Courses', 'learnpress' ),
@@ -111,18 +59,21 @@
111 59 'add_new' => __( 'Add New', 'learnpress' ),
112 60 'edit_item' => __( 'Edit Course', 'learnpress' ),
113 61 'update_item' => __( 'Update Course', 'learnpress' ),
114 62 'search_items' => __( 'Search Courses', 'learnpress' ),
115 - 'not_found' => sprintf( __( 'You haven\'t had any courses yet. Click <a href="%s">Add new</a> to start', 'learnpress' ), admin_url( 'post-new.php?post_type=lp_course' ) ),
63 + 'not_found' => sprintf(
64 + __( 'You have not had any courses yet. Click <a href="%s">Add new</a> to start', 'learnpress' ),
65 + admin_url( 'post-new.php?post_type=lp_course' )
66 + ),
116 67 'not_found_in_trash' => __( 'There was no course found in the trash', 'learnpress' ),
117 68 );
118 - $course_base = $settings->get( 'course_base' );
119 - $course_permalink = empty( $course_base ) ? _x( 'courses', 'slug', 'learnpress' ) : $course_base;
69 + $course_base = LP_Settings::get_option( 'course_base', 'courses' );
70 + $course_permalink = empty( $course_base ) ? 'courses' : $course_base;
120 71
72 + // Set to $has_archive return link to courses page, is_archive will check is true
121 73 $courses_page_id = learn_press_get_page_id( 'courses' );
74 + $has_archive = $courses_page_id ? urldecode( get_page_uri( $courses_page_id ) ) : 'courses';
122 75
123 - $has_archive = $courses_page_id && get_post( $courses_page_id ) ? urldecode( get_page_uri( $courses_page_id ) ) : 'courses';
124 -
125 76 $args = array(
126 77 'labels' => $labels,
127 78 'public' => true,
128 79 'query_var' => true,
@@ -137,9 +88,9 @@
137 88 'show_in_rest' => true,
138 89 'taxonomies' => array( 'course_category', 'course_tag' ),
139 90 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions', 'comments', 'excerpt' ),
140 91 'hierarchical' => false,
141 - 'rewrite' => $course_permalink ? array(
92 + 'rewrite' => ! empty( $course_permalink ) ? array(
142 93 'slug' => untrailingslashit( $course_permalink ),
143 94 'with_front' => false,
144 95 ) : false,
145 96 );
@@ -150,12 +101,9 @@
150 101 /**
151 102 * Register course taxonomy.
152 103 */
153 104 public function register_taxonomy() {
154 - $settings = LP_Settings::instance();
155 -
156 - $category_base = $settings->get( 'course_category_base' );
157 -
105 + $category_base = LP_Settings::get_option( 'course_category_base' );
158 106 register_taxonomy(
159 107 'course_category',
160 108 array( LP_COURSE_CPT ),
161 109 array(
@@ -176,9 +124,9 @@
176 124 'show_in_admin_bar' => true,
177 125 'show_in_nav_menus' => true,
178 126 'show_in_rest' => true,
179 127 'rewrite' => array(
180 - 'slug' => empty( $category_base ) ? _x( 'course-category', 'slug', 'learnpress' ) : $category_base,
128 + 'slug' => empty( $category_base ) ? 'course-category' : $category_base,
181 129 'hierarchical' => true,
182 130 'with_front' => false,
183 131 ),
184 132 )
@@ -183,10 +131,9 @@
183 131 ),
184 132 )
185 133 );
186 134
187 - $tag_base = $settings->get( 'course_tag_base' );
188 -
135 + $tag_base = LP_Settings::get_option( 'course_tag_base' );
189 136 register_taxonomy(
190 137 'course_tag',
191 138 array( LP_COURSE_CPT ),
192 139 array(
@@ -214,9 +161,9 @@
214 161 'update_count_callback' => '_update_post_term_count',
215 162 'query_var' => true,
216 163 'show_in_rest' => true,
217 164 'rewrite' => array(
218 - 'slug' => empty( $tag_base ) ? _x( 'course-tag', 'slug', 'learnpress' ) : $tag_base,
165 + 'slug' => empty( $tag_base ) ? 'course-tag' : $tag_base,
219 166 'with_front' => false,
220 167 ),
221 168 )
222 169 );
@@ -222,34 +169,41 @@
222 169 );
223 170 }
224 171
225 172 /**
226 - * Load data for course editor.
173 + * Declare class name of table list courses.
227 174 *
228 - * @since 3.0.0
229 - * @editor tungnx
230 - * @reason not use
175 + * @param string $class_name
176 + * @param array $args
177 + *
178 + * @return string
179 + * @since 4.2.9.5
231 180 */
232 - /*
233 - public function data_course_editor() {
234 - if ( LP_COURSE_CPT !== get_post_type() ) {
235 - return;
181 + public function wp_list_table_class_name( $class_name, $args ) {
182 + if ( $this->check_class_name_handle_table( $args['screen'] ) ) {
183 + $class_name = CoursesTable::class;
236 184 }
237 185
238 - }*/
186 + return $class_name;
187 + }
239 188
240 189 /**
241 190 * Delete course sections before delete course.
242 191 *
243 192 * @param int $post_id
193 + *
194 + * @throws Exception
195 + * @since modify 4.0.9
244 196 * @since 3.0.0
245 197 * @editor tungnx
246 - * @since modify 4.0.9
247 198 */
248 199 public function before_delete( int $post_id ) {
249 - // course curd
250 - // $curd = new LP_Course_CURD();
251 - // $curd->remove_course( $post_id );
200 + // Delete course from table learnpress_courses
201 + $courseModel = CourseModel::find( $post_id, true );
202 + if ( $courseModel ) {
203 + $courseModel->delete();
204 + }
205 +
252 206 $course = learn_press_get_course( $post_id );
253 207 if ( ! $course ) {
254 208 return;
255 209 }
@@ -256,13 +210,129 @@
256 210 $course->delete_relate_data_when_delete_course();
257 211 }
258 212
259 213 /**
260 - * @param string $fields
214 + * Query lp courses in admin via Post DB
261 215 *
262 - * @return string
216 + * @param array $posts
217 + * @param WP_Query $wp_query
218 + *
219 + * @return array|WP_Query
220 + * @since 4.4.8
221 + * @version 1.0.0
263 222 */
264 - public function posts_fields( $fields ): string {
223 + public function posts_pre_query( $posts, $wp_query ) {
224 + try {
225 + if ( ! is_admin() ) {
226 + return $posts;
227 + }
228 +
229 + // Check screen
230 + $curren_screen = get_current_screen();
231 + if ( ! $curren_screen || $curren_screen->id !== 'edit-' . LP_COURSE_CPT ) {
232 + return $posts;
233 + }
234 +
235 + $post_type = $wp_query->get( 'post_type' );
236 +
237 + if ( empty( $post_type ) || $post_type !== LP_COURSE_CPT ) {
238 + return $posts;
239 + }
240 +
241 + // Convert params from WP_Query to CoursePostFilter
242 + $posts_per_page = get_user_option( "edit_{$post_type}_per_page", get_current_user_id() );
243 + if ( empty( $posts_per_page ) ) {
244 + $posts_per_page = 20;
245 + }
246 +
247 + $paged = max( 1, get_query_var( 'paged' ) );
248 + $author = $wp_query->get( 'author' );
249 + $status = $wp_query->get( 'post_status' );
250 + $search = $wp_query->get( 's' );
251 + $month = $wp_query->get( 'm' );
252 + $orderby = LP_Request::get_param( 'orderby', '', 'key' );
253 + $order = LP_Request::get_param( 'order', '', 'key' );
254 + $filter_price = LP_Helper::sanitize_params_submitted( $_REQUEST['filter_price'] ?? '' );
255 +
256 + $filter = new CoursePostFilter();
257 + $filter->page = $paged;
258 + $filter->limit = $posts_per_page;
259 + $post_db = PostDB::getInstance();
260 +
261 + if ( ! empty( $status ) ) {
262 + $filter->post_status = array( $status );
263 + }
264 +
265 + if ( ! empty( $author ) ) {
266 + $filter->post_author = absint( $author );
267 + }
268 +
269 + if ( ! empty( $search ) ) {
270 + $filter->post_title = sanitize_text_field( wp_unslash( $search ) );
271 + }
272 +
273 + if ( ! empty( $month ) && is_numeric( $month ) && strlen( (string) $month ) === 6 ) {
274 + $filter->where[] = $post_db->wpdb->prepare(
275 + 'AND YEAR(p.post_date) = %d AND MONTH(p.post_date) = %d',
276 + substr( $month, 0, 4 ),
277 + substr( $month, 4, 2 )
278 + );
279 + }
280 +
281 + /*$has_price_filter = false;
282 + if ( array_key_exists( 'filter_price', $_REQUEST ) && $filter_price !== '' ) {
283 + $filter_price = floatval( $filter_price );
284 + $has_price_filter = true;
285 + }
286 +
287 + if ( $orderby === 'price' || $has_price_filter ) {
288 + $filter->join[] = "LEFT JOIN {$post_db->wpdb->postmeta} pm_price ON pm_price.post_id = p.ID AND pm_price.meta_key = '_lp_price'";
289 + }
290 +
291 + if ( $has_price_filter ) {
292 + if ( $filter_price == 0 ) {
293 + $filter->where[] = 'AND ( pm_price.meta_value IS NULL || pm_price.meta_value = 0 )';
294 + } else {
295 + $filter->where[] = $post_db->wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
296 + }
297 + }*/
298 +
299 + // Exclude status auto-draft
300 + $filter->where[] = $post_db->wpdb->prepare( 'AND p.post_status != %s', PostModel::STATUS_AUTO_DRAFT );
301 +
302 + if ( $orderby === 'price' ) {
303 + $filter->join[] = "LEFT JOIN {$post_db->wpdb->postmeta} pm_price
304 + ON pm_price.post_id = p.ID
305 + AND pm_price.meta_key = '_lp_price'";
306 + $filter->order_by = 'CAST(pm_price.meta_value AS UNSIGNED)';
307 + } elseif ( $orderby === 'title' ) {
308 + $filter->order_by = 'p.post_title';
309 + } elseif ( $orderby === 'author' ) {
310 + $filter->order_by = 'p.post_author';
311 + } elseif ( $orderby === 'date' ) {
312 + $filter->order_by = 'p.post_date';
313 + } else {
314 + $filter->order_by = 'p.menu_order';
315 + }
316 +
317 + $filter->order = strtolower( $order ) === 'asc' ? 'ASC' : 'DESC';
318 +
319 + // Get lp courses
320 + $total_rows = 0;
321 + $lp_courses = $post_db->get_posts( $filter, $total_rows );
322 +
323 + $wp_query->post_count = count( $lp_courses );
324 + $wp_query->found_posts = $total_rows;
325 + $wp_query->max_num_pages = (int) ceil( $total_rows / $posts_per_page );
326 + $posts = $lp_courses;
327 + } catch ( Throwable $e ) {
328 + LP_Debug::error_log( $e );
329 + }
330 +
331 + return $posts;
332 + }
333 +
334 + /*public function posts_fields( $fields ): string {
265 335 if ( ! $this->is_page_list_posts_on_backend() ) {
266 336 return $fields;
267 337 }
268 338
@@ -277,9 +347,9 @@
277 347 public function _posts_join_paged_course_items( $join ) {
278 348 global $wpdb;
279 349
280 350 $course_id = $this->_filter_items_by_course();
281 - if ( $course_id || ( LP_Request::get( 'orderby' ) == 'course-name' ) ) {
351 + if ( $course_id || LP_Request::get_param( 'orderby' ) === 'course-name' ) {
282 352 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_section_items si ON {$wpdb->posts}.ID = si.item_id";
283 353 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_sections s ON s.section_id = si.section_id";
284 354 $join .= " LEFT JOIN {$wpdb->posts} c ON c.ID = s.section_course_id";
285 355 }
@@ -298,13 +368,8 @@
298 368
299 369 return $where;
300 370 }
301 371
302 - /**
303 - * @param $join
304 - *
305 - * @return string
306 - */
307 372 public function posts_join_paged( $join ) {
308 373 global $wpdb;
309 374
310 375 if ( ! $this->is_page_list_posts_on_backend() ) {
@@ -310,18 +375,17 @@
310 375 if ( ! $this->is_page_list_posts_on_backend() ) {
311 376 return $join;
312 377 }
313 378
379 + if ( ! isset( $_GET['orderby'] ) || $_GET['orderby'] !== 'price' ) {
380 + return $join;
381 + }
382 +
314 383 $join .= " LEFT JOIN {$wpdb->postmeta} pm_price ON pm_price.post_id = {$wpdb->posts}.ID AND pm_price.meta_key = '_lp_price'";
315 384
316 385 return $join;
317 386 }
318 387
319 - /**
320 - * @param $where
321 - *
322 - * @return mixed|string
323 - */
324 388 public function posts_where_paged( $where ) {
325 389 global $wpdb;
326 390
327 391 if ( ! $this->is_page_list_posts_on_backend() ) {
@@ -337,29 +401,11 @@
337 401 $where .= $wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
338 402 }
339 403 }
340 404
341 - $not_in = $wpdb->prepare(
342 - "
343 - SELECT ID
344 - FROM {$wpdb->posts} p
345 - INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = %s
346 - WHERE pm.meta_value = %s
347 - ",
348 - '_lp_preview_course',
349 - 'yes'
350 - );
351 -
352 - $where .= " AND {$wpdb->posts}.ID NOT IN( {$not_in} )";
353 -
354 405 return $where;
355 406 }
356 407
357 - /**
358 - * @param $orderby
359 - *
360 - * @return string
361 - */
362 408 public function posts_orderby( $orderby ) {
363 409 if ( ! $this->is_page_list_posts_on_backend() ) {
364 410 return $orderby;
365 411 }
@@ -366,231 +412,176 @@
366 412
367 413 $order = $this->get_order_sort();
368 414 switch ( $this->get_order_by() ) {
369 415 case 'price':
370 - $orderby = "pm_price.meta_value {$order}";
416 + $orderby = "CAST(pm_price.meta_value AS UNSIGNED) {$order}";
371 417 }
372 418
373 419 return $orderby;
374 - }
420 + }*/
375 421
376 422 /**
377 - * @param $columns
423 + * Save course post
378 424 *
379 - * @return mixed
425 + * @param int $post_id
426 + * @param WP_Post|null $post
427 + * @param bool $is_update
428 + *
429 + * @since 4.2.6.9
430 + * @version 1.0.3
380 431 */
381 - public function sortable_columns( $columns ) {
382 - $columns['instructor'] = 'author';
383 - $columns['price'] = 'price';
432 + public function save_post( int $post_id, ?WP_Post $post = null, bool $is_update = false ) {
433 + try {
434 + $wp_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
435 + // Save to table learnpress_courses
436 + LP_Install::instance()->create_table_courses();
437 + if ( empty( $post ) ) {
438 + $post = get_post( $post_id );
439 + }
384 440
385 - return $columns;
386 - }
441 + /*if ( $post->post_status === 'auto-draft' ) {
442 + return;
443 + }*/
387 444
388 - /**
389 - * Use when enable Gutenberg.
390 - *
391 - * @return void
392 - */
393 - public function admin_editor() {
394 - $course = LP_Course::get_course();
445 + $courseModel = CourseModel::find( $post_id, true );
446 + if ( ! $courseModel ) {
447 + $courseModel = new CourseModel( $post );
448 + }
395 449
396 - learn_press_admin_view( 'course/editor' );
397 - }
450 + // Get all metadata of post
451 + $all_metadata = $courseModel->get_post_model()->get_all_metadata();
452 + $courseModel->meta_data = $all_metadata;
453 + // Temporary unset _elementor_page_assets of El, reason by method get_all_metadata get not use maybe_unserialize, make save invalid serialize
454 + $courseModel->meta_data->_elementor_page_assets = [];
398 455
399 - /**
400 - * Delete all sections in a course and reset auto increment
401 - */
402 - private function _reset_sections() {
403 - global $wpdb, $post;
456 + // Merge object post and courseModel
457 + $new_obj = (array) $post;
458 + $old_obj = (array) $courseModel;
459 + $old_now = array_merge( $old_obj, $new_obj );
460 + $courseModel = new CourseModel( $old_now );
404 461
405 - $wpdb->query(
406 - $wpdb->prepare(
407 - "
408 - DELETE FROM si
409 - USING {$wpdb->learnpress_section_items} si
410 - INNER JOIN {$wpdb->learnpress_sections} s ON s.section_id = si.section_id
411 - INNER JOIN {$wpdb->posts} p ON p.ID = s.section_course_id
412 - WHERE p.ID = %d
413 - ",
414 - $post->ID
415 - )
416 - );
417 - $wpdb->query(
418 - "
419 - ALTER TABLE {$wpdb->learnpress_section_items} AUTO_INCREMENT = 1
420 - "
421 - );
462 + // Save option single course
463 + include_once LP_PLUGIN_PATH . 'inc/admin/class-lp-admin.php';
464 + $lp_meta_box_course = new LP_Meta_Box_Course();
465 + $ground_fields = $lp_meta_box_course->metabox( $courseModel->ID );
466 + // Save meta fields
467 + foreach ( $ground_fields as $fields ) {
468 + if ( ! isset( $fields['content'] ) ) {
469 + continue;
470 + }
471 + foreach ( $fields['content'] as $meta_key => $option ) {
472 + $option->id = $meta_key;
473 + if ( ! $option instanceof LP_Meta_Box_Field ) {
474 + continue;
475 + }
422 476
423 - $wpdb->query(
424 - $wpdb->prepare(
425 - "
426 - DELETE FROM {$wpdb->learnpress_sections}
427 - WHERE section_course_id = %d
428 - ",
429 - $post->ID
430 - )
431 - );
432 - $wpdb->query(
433 - "
434 - ALTER TABLE {$wpdb->learnpress_sections} AUTO_INCREMENT = 1
435 - "
436 - );
437 - }
477 + if ( isset( $_POST[ $meta_key ] ) && ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
478 + $value_saved = $option->save( $courseModel->ID );
479 + if ( ! empty( $value_saved ) ) {
480 + $courseModel->meta_data->{$meta_key} = $value_saved;
481 + } else {
482 + $courseModel->meta_data->{$meta_key} = maybe_unserialize(
483 + get_post_meta( $courseModel->ID, $meta_key, true )
484 + );
485 + }
486 + } elseif ( ! $is_update ) {
487 + if ( isset( $courseModel->meta_data->{$meta_key} ) ) {
488 + continue;
489 + }
490 + $courseModel->meta_data->{$meta_key} = $option->default ?? '';
491 + } elseif ( ! empty( $wp_screen ) && LP_COURSE_CPT === $wp_screen->id ) {
492 + $value_saved = $option->save( $courseModel->ID );
493 + $courseModel->meta_data->{$meta_key} = $value_saved;
494 + }
495 + }
496 + }
438 497
439 - /**
440 - * Add columns to admin manage course page
441 - *
442 - * @param array $columns
443 - *
444 - * @return array
445 - */
446 - public function columns_head( $columns ) {
447 - $user = wp_get_current_user();
448 - $keys = array_keys( $columns );
449 - $values = array_values( $columns );
450 - $pos = array_search( 'title', $keys );
498 + // For case bulk edit multiple courses change author
499 + $bulk_edit = LP_Request::get_param( 'bulk_edit', false );
500 + if ( ! empty( $bulk_edit ) ) {
501 + $post_author = LP_Request::get_param( 'post_author', 0, 'int' );
502 + if ( $post_author != -1 ) { // -1 is for no change author
503 + $courseModel->post_author = $post_author;
504 + }
505 + } elseif ( ! empty( $_REQUEST['_post_author'] ) ) {
506 + // Not use key 'post_author' on single edit course, it special key of WP, Gutenberg save will not submit this key.
507 + $courseModel->post_author = LP_Request::get_param( '_post_author', 0, 'int' );
508 + /**
509 + * Save author to post table
510 + * Not use PostModel::save() or CoursePostModel::save() here
511 + * because it calls wp_update_post(), has hook 'save_post', it will cause infinite loop
512 + */
513 + $lp_db = LP_Database::getInstance();
514 + $filter_update = new LP_Post_Type_Filter();
515 + $filter_update->collection = $lp_db->tb_posts;
516 + $filter_update->set[] = "post_author = {$courseModel->post_author}";
517 + $filter_update->where[] = $lp_db->wpdb->prepare( 'AND ID = %d', $courseModel->ID );
518 + $lp_db->update_execute( $filter_update );
519 + clean_post_cache( $post->ID );
520 + } elseif ( isset( $_POST['post_author'] ) && isset( $_POST['screen'] ) && $_POST['screen'] === 'edit-' . LP_COURSE_CPT ) {
521 + // For case quick edit change author.
522 + $courseModel->post_author = LP_Request::get_param( 'post_author', 0, 'int' );
523 + }
451 524
452 - if ( ! empty( $columns['author'] ) ) {
453 - unset( $columns['author'] );
454 - }
525 + $this->save_price( $courseModel );
526 + $courseModel->save();
527 + // End save to table learnpress_courses
455 528
456 - if ( $pos !== false ) {
457 - array_splice( $keys, $pos + 1, 0, array( 'instructor', 'sections', 'students', 'price' ) );
458 - array_splice(
459 - $values,
460 - $pos + 1,
461 - 0,
529 + // Save extra info course
530 + // Save in background.
531 + $bg = LP_Background_Single_Course::instance();
532 + $bg->data(
462 533 array(
463 - esc_html__( 'Author', 'learnpress' ),
464 - esc_html__( 'Content', 'learnpress' ),
465 - esc_html__( 'Students', 'learnpress' ),
466 - esc_html__( 'Price', 'learnpress' ),
534 + 'handle_name' => 'save_post',
535 + 'course_id' => $post_id,
536 + 'data' => $_POST ?? [],
467 537 )
468 - );
469 -
470 - if ( $pos === 0 ) {
471 - array_unshift( $keys, 'thumbnail' );
472 - array_unshift( $values, esc_html__( 'Thumbnail', 'learnpress' ) );
473 - } else {
474 - array_splice( $keys, $pos, 0, array( 'thumbnail' ) );
475 - array_splice( $values, $pos, 0, array( esc_html__( 'Thumbnail', 'learnpress' ) ) );
476 - }
477 -
478 - $columns = array_combine( $keys, $values );
479 - } else {
480 - $columns['instructor'] = esc_html__( 'Author', 'learnpress' );
481 - $columns['sections'] = esc_html__( 'Content', 'learnpress' );
482 - $columns['students'] = esc_html__( 'Students', 'learnpress' );
483 - $columns['price'] = esc_html__( 'Price', 'learnpress' );
538 + )->dispatch();
539 + } catch ( Throwable $e ) {
540 + LP_Debug::error_log( $e );
484 541 }
485 -
486 - $columns['taxonomy-course_category'] = esc_html__( 'Categories', 'learnpress' );
487 -
488 - if ( in_array( 'lp_teacher', $user->roles ) ) {
489 - unset( $columns['instructor'] );
490 - }
491 -
492 - return apply_filters( 'lp/admin/courses/columns', $columns );
493 542 }
494 543
495 544 /**
496 - * Print content for custom column
545 + * Save price course
497 546 *
498 - * @param string
499 - * @param int
547 + * @return void
548 + * @throws Exception
500 549 */
501 - public function columns_content( $column, $post_id = 0 ) {
502 - global $post;
550 + protected function save_price( CourseModel &$courseObj ) {
551 + $coursePost = new CoursePostModel( $courseObj );
503 552
504 - $course = learn_press_get_course( $post->ID );
553 + $regular_price = $courseObj->get_regular_price();
554 + $sale_price = $courseObj->get_sale_price();
555 + if ( (float) $regular_price < 0 ) {
556 + $courseObj->meta_data->{CoursePostModel::META_KEY_REGULAR_PRICE} = '';
557 + $regular_price = $courseObj->get_regular_price();
558 + }
505 559
506 - switch ( $column ) {
507 - case 'thumbnail':
508 - echo wp_kses_post( $course->get_image( 'thumbnail' ) );
509 - break;
510 - case 'instructor':
511 - $this->column_instructor( $post->ID );
512 - break;
513 - case 'sections':
514 - $curd = new LP_Course_CURD();
515 - $number_sections = $curd->count_sections( $post_id );
516 - if ( $number_sections ) {
517 - $output = sprintf( _n( '<strong>%d</strong> section', '<strong>%d</strong> sections', $number_sections, 'learnpress' ), $number_sections );
518 - $html_items = array();
519 - $post_types = get_post_types( null, 'objects' );
560 + if ( $sale_price !== '' && (float) $sale_price > (float) $regular_price ) {
561 + $courseObj->meta_data->{CoursePostModel::META_KEY_SALE_PRICE} = '';
562 + $sale_price = $courseObj->get_sale_price();
563 + }
520 564
521 - foreach ( learn_press_get_course_item_types() as $item_type ) {
522 - $count_item = $course->count_items( $item_type );
565 + // Save sale regular price and sale price to table postmeta
566 + $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_REGULAR_PRICE, $regular_price );
567 + $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_SALE_PRICE, $sale_price );
523 568
524 - if ( ! $count_item ) {
525 - continue;
526 - }
527 -
528 - $post_type_object = $post_types[ $item_type ];
529 - $singular_name = $post_type_object->labels->singular_name;
530 - $plural_name = $post_type_object->label;
531 - $html_items[] = sprintf( _n( '<strong>%d</strong> ' . $singular_name, '<strong>%d</strong> ' . $plural_name, $count_item, 'learnpress' ), $count_item );
532 - }
533 -
534 - $html_items = apply_filters( 'learn-press/course-count-items', $html_items );
535 -
536 - if ( $html_items ) {
537 - $output .= ' (' . implode( ', ', $html_items ) . ')';
538 - }
539 -
540 - echo wp_kses_post( $output );
541 - } else {
542 - esc_html_e( 'No content', 'learnpress' );
543 - }
544 -
545 - break;
546 -
547 - case 'price':
548 - echo wp_kses_post( $course->get_course_price_html() );
549 - break;
550 - case 'students':
551 - $count = $course->get_total_user_enrolled_or_purchased();
552 - echo '<span class="lp-label-counter' . ( ! $count ? ' disabled' : '' ) . '">' . ( $count ? $count : 0 ) . '</span>';
553 - break;
569 + $has_sale = $courseObj->has_sale_price();
570 + if ( $has_sale ) {
571 + $courseObj->is_sale = 1;
572 + $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_IS_SALE, 1 );
573 + } else {
574 + $courseObj->is_sale = 0;
575 + delete_post_meta( $courseObj->get_id(), CoursePostModel::META_KEY_IS_SALE );
554 576 }
555 - }
556 577
557 - public function meta_boxes() {
558 - return array(
559 - 'course-editor' => array(
560 - 'title' => esc_html__( 'Curriculum', 'learnpress' ),
561 - 'callback' => array( $this, 'admin_editor' ),
562 - 'context' => 'normal',
563 - 'priority' => 'high',
564 - ),
565 - );
578 + // Set price to sort on lists.
579 + $courseObj->price_to_sort = $courseObj->get_price();
580 + $coursePost->save_meta_value_by_key( CoursePostModel::META_KEY_PRICE, $courseObj->price_to_sort );
566 581 }
567 582
568 583 /**
569 - * Save course post
570 - * Should write run background if handle big and need more time
571 - *
572 - * @param int $post_id
573 - * @param WP_Post $post
574 - * @since 4.0.9
575 - * @version 1.0.0
576 - * @editor tungnx
577 - * @see LP_Background_Single_Course::handle()
578 - */
579 - public function save( int $post_id = 0, WP_Post $post = null ) {
580 - // Save in background.
581 - $bg = LP_Background_Single_Course::instance();
582 -
583 - $bg->data(
584 - array(
585 - 'handle_name' => 'save_post',
586 - 'course_id' => $post_id,
587 - 'data' => $_POST ?? array(),
588 - )
589 - )->dispatch();
590 - }
591 -
592 - /**
593 584 * Instance LP_Course_Post_Type.
594 585 *
595 586 * @return LP_Course_Post_Type|null
596 587 */
@@ -603,7 +594,5 @@
603 594 }
604 595 }
605 596
606 597 $course_post_type = LP_Course_Post_Type::instance();
607 -
608 - // $course_post_type->add_meta_box( 'course-editor', esc_html__( 'Curriculum', 'learnpress' ), 'admin_editor', 'normal', 'high' );
609 598 }