PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.6.9.1
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.6.9.1
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
learnpress / inc / custom-post-types / course.php

course.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.1.6.9.1, at inc/custom-post-types/course.php

680 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class LP_Course_Post_Type
4 *
5 * @author ThimPress
6 * @package LearnPress/Classes
7 * @version 3.0.0
8 */
9
10 defined( 'ABSPATH' ) || exit();
11
12 if ( ! class_exists( 'LP_Course_Post_Type' ) ) {
13
14 /**
15 * Class LP_Course_Post_Type
16 */
17 final class LP_Course_Post_Type extends LP_Abstract_Post_Type {
18 /**
19 * @var null
20 */
21 protected static $_instance = null;
22
23 /**
24 * @var string
25 */
26 protected $_post_type = LP_COURSE_CPT;
27
28 /**
29 * Constructor
30 *
31 * @param string
32 */
33 public function __construct() {
34 parent::__construct();
35
36 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' ) );
43 }
44
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 /**
100 * Register course post type.
101 */
102 public function args_register_post_type() : array {
103 $settings = LP_Settings::instance();
104 $labels = array(
105 'name' => _x( 'Courses', 'Post Type General Name', 'learnpress' ),
106 'singular_name' => _x( 'Course', 'Post Type Singular Name', 'learnpress' ),
107 'menu_name' => __( 'Courses', 'learnpress' ),
108 'parent_item_colon' => __( 'Parent Item:', 'learnpress' ),
109 'all_items' => __( 'Courses', 'learnpress' ),
110 'view_item' => __( 'View Course', 'learnpress' ),
111 'add_new_item' => __( 'Add New Course', 'learnpress' ),
112 'add_new' => __( 'Add New', 'learnpress' ),
113 'edit_item' => __( 'Edit Course', 'learnpress' ),
114 'update_item' => __( 'Update Course', 'learnpress' ),
115 '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' ),
118 );
119 $course_base = $settings->get( 'course_base' );
120 $course_permalink = empty( $course_base ) ? _x( 'courses', 'slug', 'learnpress' ) : $course_base;
121
122 $courses_page_id = learn_press_get_page_id( 'courses' );
123
124 $has_archive = $courses_page_id && get_post( $courses_page_id ) ? urldecode( get_page_uri( $courses_page_id ) ) : 'courses';
125
126 $args = array(
127 'labels' => $labels,
128 'public' => true,
129 'query_var' => true,
130 'publicly_queryable' => true,
131 'show_ui' => true,
132 'has_archive' => $has_archive,
133 'capability_type' => $this->_post_type,
134 'map_meta_cap' => true,
135 'show_in_menu' => 'learn_press',
136 'show_in_admin_bar' => true,
137 'show_in_nav_menus' => true,
138 'show_in_rest' => true,
139 'taxonomies' => array( 'course_category', 'course_tag' ),
140 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions', 'comments', 'excerpt' ),
141 'hierarchical' => false,
142 'rewrite' => $course_permalink ? array(
143 'slug' => untrailingslashit( $course_permalink ),
144 'with_front' => false,
145 ) : false,
146 );
147
148 return $args;
149 }
150
151 /**
152 * Register course taxonomy.
153 */
154 public function register_taxonomy() {
155 $settings = LP_Settings::instance();
156
157 $category_base = $settings->get( 'course_category_base' );
158
159 register_taxonomy(
160 'course_category',
161 array( LP_COURSE_CPT ),
162 array(
163 'label' => __( 'Course Categories', 'learnpress' ),
164 'labels' => array(
165 'name' => __( 'Course Categories', 'learnpress' ),
166 'menu_name' => __( 'Course Category', 'learnpress' ),
167 'singular_name' => __( 'Category', 'learnpress' ),
168 'add_new_item' => __( 'Add New Course Category', 'learnpress' ),
169 'all_items' => __( 'All Categories', 'learnpress' ),
170 ),
171 'query_var' => true,
172 'public' => true,
173 'hierarchical' => true,
174 'show_ui' => true,
175 'show_in_menu' => 'learn_press',
176 'show_admin_column' => true,
177 'show_in_admin_bar' => true,
178 'show_in_nav_menus' => true,
179 'show_in_rest' => true,
180 'rewrite' => array(
181 'slug' => empty( $category_base ) ? _x( 'course-category', 'slug', 'learnpress' ) : $category_base,
182 'hierarchical' => true,
183 'with_front' => false,
184 ),
185 )
186 );
187
188 $tag_base = $settings->get( 'course_tag_base' );
189
190 register_taxonomy(
191 'course_tag',
192 array( LP_COURSE_CPT ),
193 array(
194 'labels' => array(
195 'name' => __( 'Course Tags', 'learnpress' ),
196 'singular_name' => __( 'Tag', 'learnpress' ),
197 'search_items' => __( 'Search Course Tags', 'learnpress' ),
198 'popular_items' => __( 'Popular Course Tags', 'learnpress' ),
199 'all_items' => __( 'All Course Tags', 'learnpress' ),
200 'parent_item' => null,
201 'parent_item_colon' => null,
202 'edit_item' => __( 'Edit Course Tag', 'learnpress' ),
203 'update_item' => __( 'Update Course Tag', 'learnpress' ),
204 'add_new_item' => __( 'Add New Course Tag', 'learnpress' ),
205 'new_item_name' => __( 'New Course Tag Name', 'learnpress' ),
206 'separate_items_with_commas' => __( 'Separate tags with commas', 'learnpress' ),
207 'add_or_remove_items' => __( 'Add or remove tags', 'learnpress' ),
208 'choose_from_most_used' => __( 'Choose from the most used tags', 'learnpress' ),
209 'menu_name' => __( 'Course Tags', 'learnpress' ),
210 ),
211 'public' => true,
212 'hierarchical' => false,
213 'show_ui' => true,
214 'show_in_menu' => 'learn_press',
215 'update_count_callback' => '_update_post_term_count',
216 'query_var' => true,
217 'show_in_rest' => true,
218 'rewrite' => array(
219 'slug' => empty( $tag_base ) ? _x( 'course-tag', 'slug', 'learnpress' ) : $tag_base,
220 'with_front' => false,
221 ),
222 )
223 );
224 }
225
226 /**
227 * Load data for course editor.
228 *
229 * @since 3.0.0
230 * @editor tungnx
231 * @reason not use
232 */
233 /*
234 public function data_course_editor() {
235 if ( LP_COURSE_CPT !== get_post_type() ) {
236 return;
237 }
238
239 }*/
240
241 /**
242 * Delete course sections before delete course.
243 *
244 * @param int $post_id
245 * @since 3.0.0
246 * @editor tungnx
247 * @since modify 4.0.9
248 */
249 public function before_delete( int $post_id ) {
250 // course curd
251 // $curd = new LP_Course_CURD();
252 // $curd->remove_course( $post_id );
253 $course = learn_press_get_course( $post_id );
254 if ( ! $course ) {
255 return;
256 }
257 $course->delete_relate_data_when_delete_course();
258 }
259
260 /**
261 * @param string $fields
262 *
263 * @return string
264 */
265 public function posts_fields( $fields ): string {
266 if ( ! $this->is_page_list_posts_on_backend() ) {
267 return $fields;
268 }
269
270 $fields = ' DISTINCT ' . $fields;
271 if ( $this->get_order_by() == 'price' ) {
272 $fields .= ', pm_price.meta_value as course_price';
273 }
274
275 return $fields;
276 }
277
278 public function _posts_join_paged_course_items( $join ) {
279 global $wpdb;
280
281 $course_id = $this->_filter_items_by_course();
282 if ( $course_id || ( LP_Request::get( 'orderby' ) == 'course-name' ) ) {
283 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_section_items si ON {$wpdb->posts}.ID = si.item_id";
284 $join .= " LEFT JOIN {$wpdb->prefix}learnpress_sections s ON s.section_id = si.section_id";
285 $join .= " LEFT JOIN {$wpdb->posts} c ON c.ID = s.section_course_id";
286 }
287
288 return $join;
289 }
290
291 public function _posts_where_paged_course_items( $where ) {
292 global $wpdb;
293
294 $course_id = $this->_filter_items_by_course();
295 if ( $course_id ) {
296 $where .= $wpdb->prepare( ' AND (c.ID = %d)', $course_id );
297 $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_status = %s", 'publish' );
298 }
299
300 return $where;
301 }
302
303 /**
304 * @param $join
305 *
306 * @return string
307 */
308 public function posts_join_paged( $join ) {
309 global $wpdb;
310
311 if ( ! $this->is_page_list_posts_on_backend() ) {
312 return $join;
313 }
314
315 $join .= " LEFT JOIN {$wpdb->postmeta} pm_price ON pm_price.post_id = {$wpdb->posts}.ID AND pm_price.meta_key = '_lp_price'";
316
317 return $join;
318 }
319
320 /**
321 * @param $where
322 *
323 * @return mixed|string
324 */
325 public function posts_where_paged( $where ) {
326 global $wpdb;
327
328 if ( ! $this->is_page_list_posts_on_backend() ) {
329 return $where;
330 }
331
332 $filter_price = LP_Helper::sanitize_params_submitted( $_REQUEST['filter_price'] ?? 0 );
333
334 if ( array_key_exists( 'filter_price', $_REQUEST ) ) {
335 if ( $filter_price == 0 ) {
336 $where .= ' AND ( pm_price.meta_value IS NULL || pm_price.meta_value = 0 )';
337 } else {
338 $where .= $wpdb->prepare( ' AND ( pm_price.meta_value = %s )', $filter_price );
339 }
340 }
341
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 return $where;
356 }
357
358 /**
359 * @param $orderby
360 *
361 * @return string
362 */
363 public function posts_orderby( $orderby ) {
364 if ( ! $this->is_page_list_posts_on_backend() ) {
365 return $orderby;
366 }
367
368 $order = $this->get_order_sort();
369 switch ( $this->get_order_by() ) {
370 case 'price':
371 $orderby = "pm_price.meta_value {$order}";
372 }
373
374 return $orderby;
375 }
376
377 /**
378 * @param $columns
379 *
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.
391 *
392 * @return void
393 */
394 public function admin_editor() {
395 $course = LP_Course::get_course();
396
397 learn_press_admin_view( 'course/editor' );
398 }
399
400 /**
401 * Delete all sections in a course and reset auto increment
402 */
403 private function _reset_sections() {
404 global $wpdb, $post;
405
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 );
423
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 }
439
440 private function _send_mail() {
441 if ( ! LP()->user->is_instructor() ) {
442 return;
443 }
444 $mail = LP()->mail;
445
446 }
447
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 ] );
487 }
488 }
489 }*/
490
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 );
505
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,
532 array(
533 esc_html__( 'Author', 'learnpress' ),
534 esc_html__( 'Content', 'learnpress' ),
535 esc_html__( 'Students', 'learnpress' ),
536 esc_html__( 'Price', 'learnpress' ),
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' );
554 }
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 }
564
565 /**
566 * Print content for custom column
567 *
568 * @param string
569 * @param int
570 */
571 public function columns_content( $column, $post_id = 0 ) {
572 global $post;
573
574 $course = learn_press_get_course( $post->ID );
575
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' );
590
591 foreach ( learn_press_get_course_item_types() as $item_type ) {
592 $count_item = $course->count_items( $item_type );
593
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;
624 }
625 }
626
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 );
636 }
637
638 /**
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 * Instance LP_Course_Post_Type.
664 *
665 * @return LP_Course_Post_Type|null
666 */
667 public static function instance() {
668 if ( ! self::$_instance ) {
669 self::$_instance = new self();
670 }
671
672 return self::$_instance;
673 }
674 }
675
676 $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 }
680