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