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 +311 -392 4.1.6.94.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,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,129 @@
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|WP_Query
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 + $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 {
266 335 if ( ! $this->is_page_list_posts_on_backend() ) {
267 336 return $fields;
268 337 }
269 338
@@ -278,9 +347,9 @@
278 347 public function _posts_join_paged_course_items( $join ) {
279 348 global $wpdb;
280 349
281 350 $course_id = $this->_filter_items_by_course();
282 - if ( $course_id || ( LP_Request::get( 'orderby' ) == 'course-name' ) ) {
351 + if ( $course_id || LP_Request::get_param( 'orderby' ) === 'course-name' ) {
283 352 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_section_items si ON {$wpdb->posts}.ID = si.item_id";
284 353 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_sections s ON s.section_id = si.section_id";
285 354 $join .= " LEFT JOIN {$wpdb->posts} c ON c.ID = s.section_course_id";
286 355 }
@@ -299,13 +368,8 @@
299 368
300 369 return $where;
301 370 }
302 371
303 - /**
304 - * @param $join
305 - *
306 - * @return string
307 - */
308 372 public function posts_join_paged( $join ) {
309 373 global $wpdb;
310 374
311 375 if ( ! $this->is_page_list_posts_on_backend() ) {
@@ -311,18 +375,17 @@
311 375 if ( ! $this->is_page_list_posts_on_backend() ) {
312 376 return $join;
313 377 }
314 378
379 + if ( ! isset( $_GET['orderby'] ) || $_GET['orderby'] !== 'price' ) {
380 + return $join;
381 + }
382 +
315 383 $join .= " LEFT JOIN {$wpdb->postmeta} pm_price ON pm_price.post_id = {$wpdb->posts}.ID AND pm_price.meta_key = '_lp_price'";
316 384
317 385 return $join;
318 386 }
319 387
320 - /**
321 - * @param $where
322 - *
323 - * @return mixed|string
324 - */
325 388 public function posts_where_paged( $where ) {
326 389 global $wpdb;
327 390
328 391 if ( ! $this->is_page_list_posts_on_backend() ) {
@@ -338,29 +401,11 @@
338 401 $where .= $wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
339 402 }
340 403 }
341 404
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 405 return $where;
356 406 }
357 407
358 - /**
359 - * @param $orderby
360 - *
361 - * @return string
362 - */
363 408 public function posts_orderby( $orderby ) {
364 409 if ( ! $this->is_page_list_posts_on_backend() ) {
365 410 return $orderby;
366 411 }
@@ -367,300 +412,176 @@
367 412
368 413 $order = $this->get_order_sort();
369 414 switch ( $this->get_order_by() ) {
370 415 case 'price':
371 - $orderby = "pm_price.meta_value {$order}";
416 + $orderby = "CAST(pm_price.meta_value AS UNSIGNED) {$order}";
372 417 }
373 418
374 419 return $orderby;
375 - }
420 + }*/
376 421
377 422 /**
378 - * @param $columns
423 + * Save course post
379 424 *
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.
425 + * @param int $post_id
426 + * @param WP_Post|null $post
427 + * @param bool $is_update
391 428 *
392 - * @return void
429 + * @since 4.2.6.9
430 + * @version 1.0.3
393 431 */
394 - public function admin_editor() {
395 - $course = LP_Course::get_course();
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 + }
396 440
397 - learn_press_admin_view( 'course/editor' );
398 - }
441 + /*if ( $post->post_status === 'auto-draft' ) {
442 + return;
443 + }*/
399 444
400 - /**
401 - * Delete all sections in a course and reset auto increment
402 - */
403 - private function _reset_sections() {
404 - global $wpdb, $post;
445 + $courseModel = CourseModel::find( $post_id, true );
446 + if ( ! $courseModel ) {
447 + $courseModel = new CourseModel( $post );
448 + }
405 449
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 - );
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 = [];
423 455
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 - }
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 );
439 461
440 - private function _send_mail() {
441 - if ( ! LP()->user->is_instructor() ) {
442 - return;
443 - }
444 - $mail = LP()->mail;
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 + }
445 476
446 - }
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 + }
447 497
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 ] );
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' );
487 523 }
488 - }
489 - }*/
490 524
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 );
525 + $this->save_price( $courseModel );
526 + $courseModel->save();
527 + // End save to table learnpress_courses
505 528
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,
529 + // Save extra info course
530 + // Save in background.
531 + $bg = LP_Background_Single_Course::instance();
532 + $bg->data(
532 533 array(
533 - esc_html__( 'Author', 'learnpress' ),
534 - esc_html__( 'Content', 'learnpress' ),
535 - esc_html__( 'Students', 'learnpress' ),
536 - esc_html__( 'Price', 'learnpress' ),
534 + 'handle_name' => 'save_post',
535 + 'course_id' => $post_id,
536 + 'data' => $_POST ?? [],
537 537 )
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' );
538 + )->dispatch();
539 + } catch ( Throwable $e ) {
540 + LP_Debug::error_log( $e );
554 541 }
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 542 }
564 543
565 544 /**
566 - * Print content for custom column
545 + * Save price course
567 546 *
568 - * @param string
569 - * @param int
547 + * @return void
548 + * @throws Exception
570 549 */
571 - public function columns_content( $column, $post_id = 0 ) {
572 - global $post;
550 + protected function save_price( CourseModel &$courseObj ) {
551 + $coursePost = new CoursePostModel( $courseObj );
573 552
574 - $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 + }
575 559
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' );
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 + }
590 564
591 - foreach ( learn_press_get_course_item_types() as $item_type ) {
592 - $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 );
593 568
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;
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 );
624 576 }
625 - }
626 577
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 - );
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 );
636 581 }
637 582
638 583 /**
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 584 * Instance LP_Course_Post_Type.
664 585 *
665 586 * @return LP_Course_Post_Type|null
666 587 */
@@ -673,7 +594,5 @@
673 594 }
674 595 }
675 596
676 597 $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 598 }