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 -319 4.1.7.3.24.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,75 +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_trash_item' => __( 'Do you want to remove the "{{ITEM_NAME}}" item from the trash?', 'learnpress' ),
76 - 'item_labels' => array(
77 - 'singular' => __( 'Item', 'learnpress' ),
78 - 'plural' => __( 'Items', 'learnpress' ),
79 - ),
80 - 'notice_sale_price' => __( 'The course sale price must be less than the regular price', 'learnpress' ),
81 - 'notice_price' => __( 'The course price must be greater than the sale price', 'learnpress' ),
82 - 'notice_sale_start_date' => __( 'The sale start date must be before the sale end date', 'learnpress' ),
83 - 'notice_sale_end_date' => __( 'The sale end date must be after the sale start date', 'learnpress' ),
84 - 'notice_invalid_date' => __( 'Invalid date', 'learnpress' ),
85 - ),
86 - 'sections' => array(
87 - 'sections' => $course->get_curriculum_raw(),
88 - 'hidden_sections' => ! empty( $hidden_sections ) ? $hidden_sections : array(),
89 - 'urlEdit' => admin_url( 'post.php?action=edit&post=' ),
90 - ),
91 - )
92 - );
93 -
94 - learn_press_admin_assets()->add_script_data( 'learn-press-admin-course-editor', $data );
95 - }
96 -
97 44 /**
98 45 * Register course post type.
99 46 */
100 - public function args_register_post_type() : array {
101 - $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 +
102 51 $labels = array(
103 52 'name' => _x( 'Courses', 'Post Type General Name', 'learnpress' ),
104 53 'singular_name' => _x( 'Course', 'Post Type Singular Name', 'learnpress' ),
105 54 'menu_name' => __( 'Courses', 'learnpress' ),
@@ -110,18 +59,21 @@
110 59 'add_new' => __( 'Add New', 'learnpress' ),
111 60 'edit_item' => __( 'Edit Course', 'learnpress' ),
112 61 'update_item' => __( 'Update Course', 'learnpress' ),
113 62 'search_items' => __( 'Search Courses', 'learnpress' ),
114 - '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 + ),
115 67 'not_found_in_trash' => __( 'There was no course found in the trash', 'learnpress' ),
116 68 );
117 - $course_base = $settings->get( 'course_base' );
118 - $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;
119 71
72 + // Set to $has_archive return link to courses page, is_archive will check is true
120 73 $courses_page_id = learn_press_get_page_id( 'courses' );
74 + $has_archive = $courses_page_id ? urldecode( get_page_uri( $courses_page_id ) ) : 'courses';
121 75
122 - $has_archive = $courses_page_id && get_post( $courses_page_id ) ? urldecode( get_page_uri( $courses_page_id ) ) : 'courses';
123 -
124 76 $args = array(
125 77 'labels' => $labels,
126 78 'public' => true,
127 79 'query_var' => true,
@@ -136,9 +88,9 @@
136 88 'show_in_rest' => true,
137 89 'taxonomies' => array( 'course_category', 'course_tag' ),
138 90 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions', 'comments', 'excerpt' ),
139 91 'hierarchical' => false,
140 - 'rewrite' => $course_permalink ? array(
92 + 'rewrite' => ! empty( $course_permalink ) ? array(
141 93 'slug' => untrailingslashit( $course_permalink ),
142 94 'with_front' => false,
143 95 ) : false,
144 96 );
@@ -149,12 +101,9 @@
149 101 /**
150 102 * Register course taxonomy.
151 103 */
152 104 public function register_taxonomy() {
153 - $settings = LP_Settings::instance();
154 -
155 - $category_base = $settings->get( 'course_category_base' );
156 -
105 + $category_base = LP_Settings::get_option( 'course_category_base' );
157 106 register_taxonomy(
158 107 'course_category',
159 108 array( LP_COURSE_CPT ),
160 109 array(
@@ -175,9 +124,9 @@
175 124 'show_in_admin_bar' => true,
176 125 'show_in_nav_menus' => true,
177 126 'show_in_rest' => true,
178 127 'rewrite' => array(
179 - 'slug' => empty( $category_base ) ? _x( 'course-category', 'slug', 'learnpress' ) : $category_base,
128 + 'slug' => empty( $category_base ) ? 'course-category' : $category_base,
180 129 'hierarchical' => true,
181 130 'with_front' => false,
182 131 ),
183 132 )
@@ -182,10 +131,9 @@
182 131 ),
183 132 )
184 133 );
185 134
186 - $tag_base = $settings->get( 'course_tag_base' );
187 -
135 + $tag_base = LP_Settings::get_option( 'course_tag_base' );
188 136 register_taxonomy(
189 137 'course_tag',
190 138 array( LP_COURSE_CPT ),
191 139 array(
@@ -213,9 +161,9 @@
213 161 'update_count_callback' => '_update_post_term_count',
214 162 'query_var' => true,
215 163 'show_in_rest' => true,
216 164 'rewrite' => array(
217 - 'slug' => empty( $tag_base ) ? _x( 'course-tag', 'slug', 'learnpress' ) : $tag_base,
165 + 'slug' => empty( $tag_base ) ? 'course-tag' : $tag_base,
218 166 'with_front' => false,
219 167 ),
220 168 )
221 169 );
@@ -221,34 +169,41 @@
221 169 );
222 170 }
223 171
224 172 /**
225 - * Load data for course editor.
173 + * Declare class name of table list courses.
226 174 *
227 - * @since 3.0.0
228 - * @editor tungnx
229 - * @reason not use
175 + * @param string $class_name
176 + * @param array $args
177 + *
178 + * @return string
179 + * @since 4.2.9.5
230 180 */
231 - /*
232 - public function data_course_editor() {
233 - if ( LP_COURSE_CPT !== get_post_type() ) {
234 - 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;
235 184 }
236 185
237 - }*/
186 + return $class_name;
187 + }
238 188
239 189 /**
240 190 * Delete course sections before delete course.
241 191 *
242 192 * @param int $post_id
193 + *
194 + * @throws Exception
195 + * @since modify 4.0.9
243 196 * @since 3.0.0
244 197 * @editor tungnx
245 - * @since modify 4.0.9
246 198 */
247 199 public function before_delete( int $post_id ) {
248 - // course curd
249 - // $curd = new LP_Course_CURD();
250 - // $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 +
251 206 $course = learn_press_get_course( $post_id );
252 207 if ( ! $course ) {
253 208 return;
254 209 }
@@ -255,13 +210,129 @@
255 210 $course->delete_relate_data_when_delete_course();
256 211 }
257 212
258 213 /**
259 - * @param string $fields
214 + * Query lp courses in admin via Post DB
260 215 *
261 - * @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
262 222 */
263 - 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 {
264 335 if ( ! $this->is_page_list_posts_on_backend() ) {
265 336 return $fields;
266 337 }
267 338
@@ -276,9 +347,9 @@
276 347 public function _posts_join_paged_course_items( $join ) {
277 348 global $wpdb;
278 349
279 350 $course_id = $this->_filter_items_by_course();
280 - if ( $course_id || ( LP_Request::get( 'orderby' ) == 'course-name' ) ) {
351 + if ( $course_id || LP_Request::get_param( 'orderby' ) === 'course-name' ) {
281 352 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_section_items si ON {$wpdb->posts}.ID = si.item_id";
282 353 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_sections s ON s.section_id = si.section_id";
283 354 $join .= " LEFT JOIN {$wpdb->posts} c ON c.ID = s.section_course_id";
284 355 }
@@ -297,13 +368,8 @@
297 368
298 369 return $where;
299 370 }
300 371
301 - /**
302 - * @param $join
303 - *
304 - * @return string
305 - */
306 372 public function posts_join_paged( $join ) {
307 373 global $wpdb;
308 374
309 375 if ( ! $this->is_page_list_posts_on_backend() ) {
@@ -309,18 +375,17 @@
309 375 if ( ! $this->is_page_list_posts_on_backend() ) {
310 376 return $join;
311 377 }
312 378
379 + if ( ! isset( $_GET['orderby'] ) || $_GET['orderby'] !== 'price' ) {
380 + return $join;
381 + }
382 +
313 383 $join .= " LEFT JOIN {$wpdb->postmeta} pm_price ON pm_price.post_id = {$wpdb->posts}.ID AND pm_price.meta_key = '_lp_price'";
314 384
315 385 return $join;
316 386 }
317 387
318 - /**
319 - * @param $where
320 - *
321 - * @return mixed|string
322 - */
323 388 public function posts_where_paged( $where ) {
324 389 global $wpdb;
325 390
326 391 if ( ! $this->is_page_list_posts_on_backend() ) {
@@ -336,29 +401,11 @@
336 401 $where .= $wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
337 402 }
338 403 }
339 404
340 - $not_in = $wpdb->prepare(
341 - "
342 - SELECT ID
343 - FROM {$wpdb->posts} p
344 - INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = %s
345 - WHERE pm.meta_value = %s
346 - ",
347 - '_lp_preview_course',
348 - 'yes'
349 - );
350 -
351 - $where .= " AND {$wpdb->posts}.ID NOT IN( {$not_in} )";
352 -
353 405 return $where;
354 406 }
355 407
356 - /**
357 - * @param $orderby
358 - *
359 - * @return string
360 - */
361 408 public function posts_orderby( $orderby ) {
362 409 if ( ! $this->is_page_list_posts_on_backend() ) {
363 410 return $orderby;
364 411 }
@@ -365,231 +412,176 @@
365 412
366 413 $order = $this->get_order_sort();
367 414 switch ( $this->get_order_by() ) {
368 415 case 'price':
369 - $orderby = "pm_price.meta_value {$order}";
416 + $orderby = "CAST(pm_price.meta_value AS UNSIGNED) {$order}";
370 417 }
371 418
372 419 return $orderby;
373 - }
420 + }*/
374 421
375 422 /**
376 - * @param $columns
423 + * Save course post
377 424 *
378 - * @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
379 431 */
380 - public function sortable_columns( $columns ) {
381 - $columns['instructor'] = 'author';
382 - $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 + }
383 440
384 - return $columns;
385 - }
441 + /*if ( $post->post_status === 'auto-draft' ) {
442 + return;
443 + }*/
386 444
387 - /**
388 - * Use when enable Gutenberg.
389 - *
390 - * @return void
391 - */
392 - public function admin_editor() {
393 - $course = LP_Course::get_course();
445 + $courseModel = CourseModel::find( $post_id, true );
446 + if ( ! $courseModel ) {
447 + $courseModel = new CourseModel( $post );
448 + }
394 449
395 - learn_press_admin_view( 'course/editor' );
396 - }
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 = [];
397 455
398 - /**
399 - * Delete all sections in a course and reset auto increment
400 - */
401 - private function _reset_sections() {
402 - 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 );
403 461
404 - $wpdb->query(
405 - $wpdb->prepare(
406 - "
407 - DELETE FROM si
408 - USING {$wpdb->learnpress_section_items} si
409 - INNER JOIN {$wpdb->learnpress_sections} s ON s.section_id = si.section_id
410 - INNER JOIN {$wpdb->posts} p ON p.ID = s.section_course_id
411 - WHERE p.ID = %d
412 - ",
413 - $post->ID
414 - )
415 - );
416 - $wpdb->query(
417 - "
418 - ALTER TABLE {$wpdb->learnpress_section_items} AUTO_INCREMENT = 1
419 - "
420 - );
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 + }
421 476
422 - $wpdb->query(
423 - $wpdb->prepare(
424 - "
425 - DELETE FROM {$wpdb->learnpress_sections}
426 - WHERE section_course_id = %d
427 - ",
428 - $post->ID
429 - )
430 - );
431 - $wpdb->query(
432 - "
433 - ALTER TABLE {$wpdb->learnpress_sections} AUTO_INCREMENT = 1
434 - "
435 - );
436 - }
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 + }
437 497
438 - /**
439 - * Add columns to admin manage course page
440 - *
441 - * @param array $columns
442 - *
443 - * @return array
444 - */
445 - public function columns_head( $columns ) {
446 - $user = wp_get_current_user();
447 - $keys = array_keys( $columns );
448 - $values = array_values( $columns );
449 - $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 + }
450 524
451 - if ( ! empty( $columns['author'] ) ) {
452 - unset( $columns['author'] );
453 - }
525 + $this->save_price( $courseModel );
526 + $courseModel->save();
527 + // End save to table learnpress_courses
454 528
455 - if ( $pos !== false ) {
456 - array_splice( $keys, $pos + 1, 0, array( 'instructor', 'sections', 'students', 'price' ) );
457 - array_splice(
458 - $values,
459 - $pos + 1,
460 - 0,
529 + // Save extra info course
530 + // Save in background.
531 + $bg = LP_Background_Single_Course::instance();
532 + $bg->data(
461 533 array(
462 - esc_html__( 'Author', 'learnpress' ),
463 - esc_html__( 'Content', 'learnpress' ),
464 - esc_html__( 'Students', 'learnpress' ),
465 - esc_html__( 'Price', 'learnpress' ),
534 + 'handle_name' => 'save_post',
535 + 'course_id' => $post_id,
536 + 'data' => $_POST ?? [],
466 537 )
467 - );
468 -
469 - if ( $pos === 0 ) {
470 - array_unshift( $keys, 'thumbnail' );
471 - array_unshift( $values, esc_html__( 'Thumbnail', 'learnpress' ) );
472 - } else {
473 - array_splice( $keys, $pos, 0, array( 'thumbnail' ) );
474 - array_splice( $values, $pos, 0, array( esc_html__( 'Thumbnail', 'learnpress' ) ) );
475 - }
476 -
477 - $columns = array_combine( $keys, $values );
478 - } else {
479 - $columns['instructor'] = esc_html__( 'Author', 'learnpress' );
480 - $columns['sections'] = esc_html__( 'Content', 'learnpress' );
481 - $columns['students'] = esc_html__( 'Students', 'learnpress' );
482 - $columns['price'] = esc_html__( 'Price', 'learnpress' );
538 + )->dispatch();
539 + } catch ( Throwable $e ) {
540 + LP_Debug::error_log( $e );
483 541 }
484 -
485 - $columns['taxonomy-course_category'] = esc_html__( 'Categories', 'learnpress' );
486 -
487 - if ( in_array( 'lp_teacher', $user->roles ) ) {
488 - unset( $columns['instructor'] );
489 - }
490 -
491 - return apply_filters( 'lp/admin/courses/columns', $columns );
492 542 }
493 543
494 544 /**
495 - * Print content for custom column
545 + * Save price course
496 546 *
497 - * @param string
498 - * @param int
547 + * @return void
548 + * @throws Exception
499 549 */
500 - public function columns_content( $column, $post_id = 0 ) {
501 - global $post;
550 + protected function save_price( CourseModel &$courseObj ) {
551 + $coursePost = new CoursePostModel( $courseObj );
502 552
503 - $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 + }
504 559
505 - switch ( $column ) {
506 - case 'thumbnail':
507 - echo wp_kses_post( $course->get_image( 'thumbnail' ) );
508 - break;
509 - case 'instructor':
510 - $this->column_instructor( $post->ID );
511 - break;
512 - case 'sections':
513 - $curd = new LP_Course_CURD();
514 - $number_sections = $curd->count_sections( $post_id );
515 - if ( $number_sections ) {
516 - $output = sprintf( _n( '<strong>%d</strong> section', '<strong>%d</strong> sections', $number_sections, 'learnpress' ), $number_sections );
517 - $html_items = array();
518 - $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 + }
519 564
520 - foreach ( learn_press_get_course_item_types() as $item_type ) {
521 - $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 );
522 568
523 - if ( ! $count_item ) {
524 - continue;
525 - }
526 -
527 - $post_type_object = $post_types[ $item_type ];
528 - $singular_name = $post_type_object->labels->singular_name;
529 - $plural_name = $post_type_object->label;
530 - $html_items[] = sprintf( _n( '<strong>%d</strong> ' . $singular_name, '<strong>%d</strong> ' . $plural_name, $count_item, 'learnpress' ), $count_item );
531 - }
532 -
533 - $html_items = apply_filters( 'learn-press/course-count-items', $html_items );
534 -
535 - if ( $html_items ) {
536 - $output .= ' (' . implode( ', ', $html_items ) . ')';
537 - }
538 -
539 - echo wp_kses_post( $output );
540 - } else {
541 - esc_html_e( 'No content', 'learnpress' );
542 - }
543 -
544 - break;
545 -
546 - case 'price':
547 - echo wp_kses_post( $course->get_course_price_html() );
548 - break;
549 - case 'students':
550 - $count = $course->get_total_user_enrolled_or_purchased();
551 - echo '<span class="lp-label-counter' . ( ! $count ? ' disabled' : '' ) . '">' . ( $count ? $count : 0 ) . '</span>';
552 - 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 );
553 576 }
554 - }
555 577
556 - public function meta_boxes() {
557 - return array(
558 - 'course-editor' => array(
559 - 'title' => esc_html__( 'Curriculum', 'learnpress' ),
560 - 'callback' => array( $this, 'admin_editor' ),
561 - 'context' => 'normal',
562 - 'priority' => 'high',
563 - ),
564 - );
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 );
565 581 }
566 582
567 583 /**
568 - * Save course post
569 - * Should write run background if handle big and need more time
570 - *
571 - * @param int $post_id
572 - * @param WP_Post $post
573 - * @since 4.0.9
574 - * @version 1.0.0
575 - * @editor tungnx
576 - * @see LP_Background_Single_Course::handle()
577 - */
578 - public function save( int $post_id = 0, WP_Post $post = null ) {
579 - // Save in background.
580 - $bg = LP_Background_Single_Course::instance();
581 -
582 - $bg->data(
583 - array(
584 - 'handle_name' => 'save_post',
585 - 'course_id' => $post_id,
586 - 'data' => $_POST ?? array(),
587 - )
588 - )->dispatch();
589 - }
590 -
591 - /**
592 584 * Instance LP_Course_Post_Type.
593 585 *
594 586 * @return LP_Course_Post_Type|null
595 587 */
@@ -602,7 +594,5 @@
602 594 }
603 595 }
604 596
605 597 $course_post_type = LP_Course_Post_Type::instance();
606 -
607 - // $course_post_type->add_meta_box( 'course-editor', esc_html__( 'Curriculum', 'learnpress' ), 'admin_editor', 'normal', 'high' );
608 598 }