Completed
Push — master ( f1dc7e...f45963 )
by Dwain
05:41
created

Sensei_Course::single_course_lesson_data()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4
/**
5
 * Sensei Course Class
6
 *
7
 * All functionality pertaining to the Courses Post Type in Sensei.
8
 *
9
 * @package Content
10
 * @author Automattic
11
 * @since 1.0.0
12
 */
13
class Sensei_Course {
14
15
    /**
16
     * @var $token
17
     */
18
	public $token;
19
20
    /**
21
     * @var array $meta_fields
22
     */
23
	public $meta_fields;
24
25
    /**
26
     * @var string|bool $my_courses_page reference to the sites
27
     * my courses page, false if none was set
28
     */
29
    public  $my_courses_page;
30
31
	/**
32
	 * Constructor.
33
	 * @since  1.0.0
34
	 */
35
	public function __construct () {
36
37
        $this->token = 'course';
38
39
		// Setup meta fields for this post type
40
		$this->meta_fields = array( 'course_prerequisite', 'course_featured', 'course_video_embed', 'course_woocommerce_product' );
41
		// Admin actions
42
		if ( is_admin() ) {
43
			// Metabox functions
44
            add_action( 'add_meta_boxes', array( $this, 'meta_box_setup' ), 20 );
45
			add_action( 'save_post', array( $this, 'meta_box_save' ) );
46
			// Custom Write Panel Columns
47
			add_filter( 'manage_edit-course_columns', array( $this, 'add_column_headings' ), 10, 1 );
48
			add_action( 'manage_posts_custom_column', array( $this, 'add_column_data' ), 10, 2 );
49
		} else {
50
			$this->my_courses_page = false;
51
		} // End If Statement
52
53
		// Update course completion upon completion of a lesson
54
		add_action( 'sensei_user_lesson_end', array( $this, 'update_status_after_lesson_change' ), 10, 2 );
55
		// Update course completion upon reset of a lesson
56
		add_action( 'sensei_user_lesson_reset', array( $this, 'update_status_after_lesson_change' ), 10, 2 );
57
		// Update course completion upon grading of a quiz
58
		add_action( 'sensei_user_quiz_grade', array( $this, 'update_status_after_quiz_submission' ), 10, 2 );
59
60
        // show the progress bar ont he single course page
61
        add_action( 'sensei_single_course_content_inside_before' , array( $this, 'the_progress_statement' ), 15 );
62
        add_action( 'sensei_single_course_content_inside_before' , array( $this, 'the_progress_meter' ), 16 );
63
64
        // provide an option to block all emails related to a selected course
65
        add_filter( 'sensei_send_emails', array( $this, 'block_notification_emails' ) );
66
        add_action( 'save_post', array( $this, 'save_course_notification_meta_box' ) );
67
68
        // preview lessons on the course content
69
        add_action( 'sensei_course_content_inside_after',array( $this, 'the_course_free_lesson_preview' ) );
70
71
        // the course meta
72
        add_action('sensei_course_content_inside_before', array( $this, 'the_course_meta' ) );
73
74
        // backwards compatible template hooks
75
        add_action('sensei_course_content_inside_before', array( $this, 'content_before_backwards_compatibility_hooks' ));
76
        add_action('sensei_loop_course_before', array( $this,'loop_before_backwards_compatibility_hooks' ) );
77
78
        // add the user status on the course to the markup as a class
79
        add_filter('post_class', array( __CLASS__ , 'add_course_user_status_class' ), 20, 3 );
80
81
        //filter the course query in Sensei specific instances
82
        add_filter( 'pre_get_posts', array( __CLASS__, 'course_query_filter' ) );
83
84
        //attache the sorting to the course archive
85
        add_action ( 'sensei_archive_before_course_loop' , array( 'Sensei_Course', 'course_archive_sorting' ) );
86
87
        //attach the filter links to the course archive
88
        add_action ( 'sensei_archive_before_course_loop' , array( 'Sensei_Course', 'course_archive_filters' ) );
89
90
        //filter the course query when featured filter is applied
91
        add_filter( 'pre_get_posts',  array( __CLASS__, 'course_archive_featured_filter'));
92
93
        // handle the order by title post submission
94
        add_filter( 'pre_get_posts',  array( __CLASS__, 'course_archive_order_by_title'));
95
96
        // ensure the course category page respects the manual order set for courses
97
        add_filter( 'pre_get_posts',  array( __CLASS__, 'alter_course_category_order'));
98
99
        // flush rewrite rules when saving a course
100
        add_action('save_post', array( 'Sensei_Course', 'flush_rewrite_rules' ) );
101
102
	} // End __construct()
103
104
	/**
105
	 * Fires when a quiz has been graded to check if the Course status needs changing
106
	 *
107
	 * @param type $user_id
108
	 * @param type $quiz_id
109
	 */
110
	public function update_status_after_quiz_submission( $user_id, $quiz_id ) {
111
		if ( intval( $user_id ) > 0 && intval( $quiz_id ) > 0 ) {
112
			$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
113
			$this->update_status_after_lesson_change( $user_id, $lesson_id );
114
		}
115
	}
116
117
	/**
118
	 * Fires when a lesson has changed to check if the Course status needs changing
119
	 *
120
	 * @param int $user_id
121
	 * @param int $lesson_id
122
	 */
123
	public function update_status_after_lesson_change( $user_id, $lesson_id ) {
124
		if ( intval( $user_id ) > 0 && intval( $lesson_id ) > 0 ) {
125
			$course_id = get_post_meta( $lesson_id, '_lesson_course', true );
126
			if ( intval( $course_id ) > 0 ) {
127
				// Updates the Course status and it's meta data
128
				Sensei_Utils::user_complete_course( $course_id, $user_id );
129
			}
130
		}
131
	}
132
133
	/**
134
	 * meta_box_setup function.
135
	 *
136
	 * @access public
137
	 * @return void
138
	 */
139
	public function meta_box_setup () {
140
141
		if ( Sensei_WC::is_woocommerce_active() ) {
142
			// Add Meta Box for WooCommerce Course
143
			add_meta_box( 'course-wc-product', __( 'WooCommerce Product', 'woothemes-sensei' ), array( $this, 'course_woocommerce_product_meta_box_content' ), $this->token, 'side', 'default' );
144
		} // End If Statement
145
		// Add Meta Box for Prerequisite Course
146
		add_meta_box( 'course-prerequisite', __( 'Course Prerequisite', 'woothemes-sensei' ), array( $this, 'course_prerequisite_meta_box_content' ), $this->token, 'side', 'default' );
147
		// Add Meta Box for Featured Course
148
		add_meta_box( 'course-featured', __( 'Featured Course', 'woothemes-sensei' ), array( $this, 'course_featured_meta_box_content' ), $this->token, 'side', 'default' );
149
		// Add Meta Box for Course Meta
150
		add_meta_box( 'course-video', __( 'Course Video', 'woothemes-sensei' ), array( $this, 'course_video_meta_box_content' ), $this->token, 'normal', 'default' );
151
		// Add Meta Box for Course Lessons
152
		add_meta_box( 'course-lessons', __( 'Course Lessons', 'woothemes-sensei' ), array( $this, 'course_lessons_meta_box_content' ), $this->token, 'normal', 'default' );
153
        // Add Meta Box to link to Manage Learners
154
        add_meta_box( 'course-manage', __( 'Course Management', 'woothemes-sensei' ), array( $this, 'course_manage_meta_box_content' ), $this->token, 'side', 'default' );
155
        // Remove "Custom Settings" meta box.
156
		remove_meta_box( 'woothemes-settings', $this->token, 'normal' );
157
158
        // add Disable email notification box
159
        add_meta_box( 'course-notifications', __( 'Course Notifications', 'woothemes-sensei' ), array( $this, 'course_notification_meta_box_content' ), 'course', 'normal', 'default' );
160
161
	} // End meta_box_setup()
162
163
	/**
164
	 * course_woocommerce_product_meta_box_content function.
165
	 *
166
	 * @access public
167
	 * @return void
168
	 */
169
	public function course_woocommerce_product_meta_box_content () {
170
		global $post;
171
172
		$select_course_woocommerce_product = get_post_meta( $post->ID, '_course_woocommerce_product', true );
173
174
		$post_args = array(	'post_type' 		=> array( 'product', 'product_variation' ),
175
							'posts_per_page' 		=> -1,
176
							'orderby'         	=> 'title',
177
    						'order'           	=> 'DESC',
178
    						'exclude' 			=> $post->ID,
179
    						'post_status'		=> array( 'publish', 'private', 'draft' ),
180
    						'tax_query'			=> array(
181
								array(
182
									'taxonomy'	=> 'product_type',
183
									'field'		=> 'slug',
184
									'terms'		=> array( 'variable', 'grouped' ),
185
									'operator'	=> 'NOT IN'
186
								)
187
							),
188
							'suppress_filters' 	=> 0
189
							);
190
		$posts_array = get_posts( $post_args );
191
192
		$html = '';
193
194
		$html .= '<input type="hidden" name="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" id="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" value="' . esc_attr( wp_create_nonce( plugin_basename(__FILE__) ) ) . '" />';
195
196
		if ( count( $posts_array ) > 0 ) {
197
198
			$html .= '<select id="course-woocommerce-product-options" name="course_woocommerce_product" class="chosen_select widefat">' . "\n";
199
			$html .= '<option value="-">' . __( 'None', 'woothemes-sensei' ) . '</option>';
200
				$prev_parent_id = 0;
201
				foreach ( $posts_array as $post_item ) {
202
203
					if ( 'product_variation' == $post_item->post_type ) {
204
205
						$product_object = get_product( $post_item->ID );
206
						$parent_id = wp_get_post_parent_id( $post_item->ID );
207
208 View Code Duplication
                        if( sensei_check_woocommerce_version( '2.1' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
							$formatted_variation = wc_get_formatted_variation( $product_object->variation_data, true );
210
211
						} else {
212
                            // fall back to pre wc 2.1
213
							$formatted_variation = woocommerce_get_formatted_variation( $product_object->variation_data, true );
214
215
						}
216
217
                        $product_name = ucwords( $formatted_variation );
218
                        if( empty( $product_name ) ){
219
220
                            $product_name = __( 'Variation #', 'woothemes-sensei' ) . $product_object->variation_id;
221
222
                        }
223
224
					} else {
225
226
						$parent_id = false;
227
						$prev_parent_id = 0;
228
						$product_name = $post_item->post_title;
229
230
					}
231
232
					// Show variations in groups
233 View Code Duplication
					if( $parent_id && $parent_id != $prev_parent_id ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234
235
						if( 0 != $prev_parent_id ) {
236
237
							$html .= '</optgroup>';
238
239
						}
240
						$html .= '<optgroup label="' . get_the_title( $parent_id ) . '">';
241
						$prev_parent_id = $parent_id;
242
243
					} elseif( ! $parent_id && 0 == $prev_parent_id ) {
244
245
						$html .= '</optgroup>';
246
247
					}
248
249
					$html .= '<option value="' . esc_attr( absint( $post_item->ID ) ) . '"' . selected( $post_item->ID, $select_course_woocommerce_product, false ) . '>' . esc_html( $product_name ) . '</option>' . "\n";
250
251
				} // End For Loop
252
253
			$html .= '</select>' . "\n";
254
			if ( current_user_can( 'publish_product' )) {
255
256
				$html .= '<p>' . "\n";
257
					$html .= '<a href="' . admin_url( 'post-new.php?post_type=product' ) . '" title="' . esc_attr( __( 'Add a Product', 'woothemes-sensei' ) ) . '">' . __( 'Add a Product', 'woothemes-sensei' ) . '</a>' . "\n";
258
				$html .= '</p>'."\n";
259
260
			} // End If Statement
261
262
		} else {
263
264
			if ( current_user_can( 'publish_product' )) {
265
266
				$html .= '<p>' . "\n";
267
					$html .= esc_html( __( 'No products exist yet.', 'woothemes-sensei' ) ) . '&nbsp;<a href="' . admin_url( 'post-new.php?post_type=product' ) . '" title="' . esc_attr( __( 'Add a Product', 'woothemes-sensei' ) ) . '">' . __( 'Please add some first', 'woothemes-sensei' ) . '</a>' . "\n";
268
				$html .= '</p>'."\n";
269
270
			} else {
271
272
                $html .= '<p>' . "\n";
273
					$html .= esc_html( __( 'No products exist yet.', 'woothemes-sensei' ) ) . "\n";
274
				$html .= '</p>'."\n";
275
276
			} // End If Statement
277
278
		} // End If Statement
279
280
		echo $html;
281
282
	} // End course_woocommerce_product_meta_box_content()
283
284
	/**
285
	 * course_prerequisite_meta_box_content function.
286
	 *
287
	 * @access public
288
	 * @return void
289
	 */
290
	public function course_prerequisite_meta_box_content () {
291
		global $post;
292
293
		$select_course_prerequisite = get_post_meta( $post->ID, '_course_prerequisite', true );
294
295
		$post_args = array(	'post_type' 		=> 'course',
296
							'posts_per_page' 		=> -1,
297
							'orderby'         	=> 'title',
298
    						'order'           	=> 'DESC',
299
    						'exclude' 			=> $post->ID,
300
							'suppress_filters' 	=> 0
301
							);
302
		$posts_array = get_posts( $post_args );
303
304
		$html = '';
305
306
		$html .= '<input type="hidden" name="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" id="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" value="' . esc_attr( wp_create_nonce( plugin_basename(__FILE__) ) ) . '" />';
307
308 View Code Duplication
		if ( count( $posts_array ) > 0 ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309
			$html .= '<select id="course-prerequisite-options" name="course_prerequisite" class="chosen_select widefat">' . "\n";
310
			$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
311
				foreach ($posts_array as $post_item){
312
					$html .= '<option value="' . esc_attr( absint( $post_item->ID ) ) . '"' . selected( $post_item->ID, $select_course_prerequisite, false ) . '>' . esc_html( $post_item->post_title ) . '</option>' . "\n";
313
				} // End For Loop
314
			$html .= '</select>' . "\n";
315
		} else {
316
			$html .= '<p>' . esc_html( __( 'No courses exist yet. Please add some first.', 'woothemes-sensei' ) ) . '</p>';
317
		} // End If Statement
318
319
		echo $html;
320
321
	} // End course_prerequisite_meta_box_content()
322
323
	/**
324
	 * course_featured_meta_box_content function.
325
	 *
326
	 * @access public
327
	 * @return void
328
	 */
329
	public function course_featured_meta_box_content () {
330
		global $post;
331
332
		$course_featured = get_post_meta( $post->ID, '_course_featured', true );
333
334
		$html = '';
335
336
		$html .= '<input type="hidden" name="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" id="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" value="' . esc_attr( wp_create_nonce( plugin_basename(__FILE__) ) ) . '" />';
337
338
		$checked = '';
339
		if ( isset( $course_featured ) && ( '' != $course_featured ) ) {
340
	 	    $checked = checked( 'featured', $course_featured, false );
341
	 	} // End If Statement
342
343
	 	$html .= '<input type="checkbox" name="course_featured" value="featured" ' . $checked . '>&nbsp;' . __( 'Feature this course', 'woothemes-sensei' ) . '<br>';
344
345
		echo $html;
346
347
	} // End course_featured_meta_box_content()
348
349
	/**
350
	 * course_video_meta_box_content function.
351
	 *
352
	 * @access public
353
	 * @return void
354
	 */
355
	public function course_video_meta_box_content () {
356
		global $post;
357
358
		$course_video_embed = get_post_meta( $post->ID, '_course_video_embed', true );
359
360
		$html = '';
361
362
		$html .= '<label class="screen-reader-text" for="course_video_embed">' . __( 'Video Embed Code', 'woothemes-sensei' ) . '</label>';
363
		$html .= '<textarea rows="5" cols="50" name="course_video_embed" tabindex="6" id="course-video-embed">' . $course_video_embed . '</textarea>';
364
		$html .= '<p>' .  __( 'Paste the embed code for your video (e.g. YouTube, Vimeo etc.) in the box above.', 'woothemes-sensei' ) . '</p>';
365
366
		echo $html;
367
368
	} // End course_video_meta_box_content()
369
370
	/**
371
	 * meta_box_save function.
372
	 *
373
	 * Handles saving the meta data
374
	 *
375
	 * @access public
376
	 * @param int $post_id
377
	 * @return int
378
	 */
379
	public function meta_box_save ( $post_id ) {
380
		global $post;
381
382
		/* Verify the nonce before proceeding. */
383
		if ( ( get_post_type() != $this->token ) || ! wp_verify_nonce( $_POST['woo_' . $this->token . '_noonce'], plugin_basename(__FILE__) ) ) {
384
			return $post_id;
385
		}
386
387
		/* Get the post type object. */
388
		$post_type = get_post_type_object( $post->post_type );
389
390
		/* Check if the current user has permission to edit the post. */
391
		if ( !current_user_can( $post_type->cap->edit_post, $post_id ) ) {
392
			return $post_id;
393
		} // End If Statement
394
395 View Code Duplication
		if ( 'page' == $_POST['post_type'] ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
396
			if ( ! current_user_can( 'edit_page', $post_id ) ) {
397
				return $post_id;
398
			} // End If Statement
399
		} else {
400
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
401
				return $post_id;
402
			} // End If Statement
403
		} // End If Statement
404
405
		// Save the post meta data fields
406 View Code Duplication
		if ( isset($this->meta_fields) && is_array($this->meta_fields) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
407
			foreach ( $this->meta_fields as $meta_key ) {
408
				$this->save_post_meta( $meta_key, $post_id );
409
			} // End For Loop
410
		} // End If Statement
411
412
	} // End meta_box_save()
413
414
415
	/**
416
	 * save_post_meta function.
417
	 *
418
	 * Does the save
419
	 *
420
	 * @access private
421
	 * @param string $post_key (default: '')
422
	 * @param int $post_id (default: 0)
423
	 * @return int new meta id | bool meta value saved status
424
	 */
425
	private function save_post_meta( $post_key = '', $post_id = 0 ) {
426
		// Get the meta key.
427
		$meta_key = '_' . $post_key;
428
		// Get the posted data and sanitize it for use as an HTML class.
429 View Code Duplication
		if ( 'course_video_embed' == $post_key) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
430
			$new_meta_value = esc_html( $_POST[$post_key] );
431
		} else {
432
			$new_meta_value = ( isset( $_POST[$post_key] ) ? sanitize_html_class( $_POST[$post_key] ) : '' );
433
		} // End If Statement
434
435
        // update field with the new value
436
        return update_post_meta( $post_id, $meta_key, $new_meta_value );
437
438
	} // End save_post_meta()
439
440
	/**
441
	 * course_lessons_meta_box_content function.
442
	 *
443
	 * @access public
444
	 * @return void
445
	 */
446
	public function course_lessons_meta_box_content () {
447
448
		global $post;
449
450
		// Setup Lesson Query
451
		$posts_array = array();
452
		if ( 0 < $post->ID ) {
453
454
			$posts_array = $this->course_lessons( $post->ID, 'any' );
455
456
		} // End If Statement
457
458
		$html = '';
459
		$html .= '<input type="hidden" name="' . esc_attr( 'woo_' . $this->token . '_noonce' ) . '" id="'
460
                 . esc_attr( 'woo_' . $this->token . '_noonce' )
461
                 . '" value="' . esc_attr( wp_create_nonce( plugin_basename(__FILE__) ) ) . '" />';
462
463
		if ( count( $posts_array ) > 0 ) {
464
465
			foreach ($posts_array as $post_item){
466
467
				$html .= '<p>'."\n";
468
469
					$html .= $post_item->post_title."\n";
470
					$html .= '<a href="' . esc_url( get_edit_post_link( $post_item->ID ) ) . '" title="' . esc_attr( sprintf( __( 'Edit %s', 'woothemes-sensei' ), $post_item->post_title ) ) . '" class="edit-lesson-action">' . __( 'Edit this lesson', 'woothemes-sensei' ) . '</a>';
471
472
				$html .= '</p>'."\n";
473
474
			} // End For Loop
475
476
		} else {
477
			$course_id = '';
478
			if ( 0 < $post->ID ) { $course_id = '&course_id=' . $post->ID; }
479
			$html .= '<p>' . esc_html( __( 'No lessons exist yet for this course.', 'woothemes-sensei' ) ) . "\n";
480
481
				$html .= '<a href="' . admin_url( 'post-new.php?post_type=lesson' . $course_id )
482
                         . '" title="' . esc_attr( __( 'Add a Lesson', 'woothemes-sensei' ) ) . '">'
483
                         . __( 'Please add some.', 'woothemes-sensei' ) . '</a>' . "\n";
484
485
			$html .= '</p>'."\n";
486
		} // End If Statement
487
488
		echo $html;
489
490
	} // End course_lessons_meta_box_content()
491
492
    /**
493
     * course_manage_meta_box_content function.
494
     *
495
     * @since 1.9.0
496
     * @access public
497
     * @return void
498
     */
499
500
    public function course_manage_meta_box_content () {
501
        global $post;
502
        
503
        $manage_url = esc_url( add_query_arg( array( 'page' => 'sensei_learners', 'course_id' => $post->ID, 'view' => 'learners' ), admin_url( 'admin.php') ) );
504
505
        $grading_url = esc_url( add_query_arg( array( 'page' => 'sensei_grading', 'course_id' => $post->ID, 'view' => 'learners' ), admin_url( 'admin.php') ) );
506
507
508
        echo "<ul><li><a href='$manage_url'>".__("Manage Learners", 'woothemes-sensei')."</a></li>";
509
510
        echo "<li><a href='$grading_url'>".__("Manage Grading", 'woothemes-sensei')."</a></li></ul>";
511
512
513
514
    } // End course_manage_meta_box_content()
515
516
	/**
517
	 * Add column headings to the "lesson" post list screen.
518
	 * @access public
519
	 * @since  1.0.0
520
	 * @param  array $defaults
521
	 * @return array $new_columns
522
	 */
523
	public function add_column_headings ( $defaults ) {
524
		$new_columns['cb'] = '<input type="checkbox" />';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$new_columns was never initialized. Although not strictly required by PHP, it is generally a good practice to add $new_columns = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
525
		// $new_columns['id'] = __( 'ID' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
526
		$new_columns['title'] = _x( 'Course Title', 'column name', 'woothemes-sensei' );
527
		$new_columns['course-prerequisite'] = _x( 'Pre-requisite Course', 'column name', 'woothemes-sensei' );
528
		if ( Sensei_WC::is_woocommerce_active() ) {
529
			$new_columns['course-woocommerce-product'] = _x( 'WooCommerce Product', 'column name', 'woothemes-sensei' );
530
		} // End If Statement
531
		$new_columns['course-category'] = _x( 'Category', 'column name', 'woothemes-sensei' );
532
		if ( isset( $defaults['date'] ) ) {
533
			$new_columns['date'] = $defaults['date'];
534
		}
535
536
		return $new_columns;
537
	} // End add_column_headings()
538
539
	/**
540
	 * Add data for our newly-added custom columns.
541
	 * @access public
542
	 * @since  1.0.0
543
	 * @param  string $column_name
544
	 * @param  int $id
545
	 * @return void
546
	 */
547
	public function add_column_data ( $column_name, $id ) {
548
		global $wpdb, $post;
549
550
		switch ( $column_name ) {
551
			case 'id':
552
				echo $id;
553
			break;
554
555 View Code Duplication
			case 'course-prerequisite':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
556
				$course_prerequisite_id = get_post_meta( $id, '_course_prerequisite', true);
557
				if ( 0 < absint( $course_prerequisite_id ) ) { echo '<a href="' . esc_url( get_edit_post_link( absint( $course_prerequisite_id ) ) ) . '" title="' . esc_attr( sprintf( __( 'Edit %s', 'woothemes-sensei' ), get_the_title( absint( $course_prerequisite_id ) ) ) ) . '">' . get_the_title( absint( $course_prerequisite_id ) ) . '</a>'; }
558
559
			break;
560
561
			case 'course-woocommerce-product':
562
				if ( Sensei_WC::is_woocommerce_active() ) {
563
					$course_woocommerce_product_id = get_post_meta( $id, '_course_woocommerce_product', true);
564
					if ( 0 < absint( $course_woocommerce_product_id ) ) {
565
						if ( 'product_variation' == get_post_type( $course_woocommerce_product_id ) ) {
566
							$product_object = get_product( $course_woocommerce_product_id );
567 View Code Duplication
							if( sensei_check_woocommerce_version( '2.1' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
568
								$formatted_variation = wc_get_formatted_variation( $product_object->variation_data, true );
569
							} else {
570
								$formatted_variation = woocommerce_get_formatted_variation( $product_object->variation_data, true );
571
							}
572
							$course_woocommerce_product_id = $product_object->parent->post->ID;
573
							$product_name = $product_object->parent->post->post_title . '<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . ucwords( $formatted_variation );
574
						} else {
575
							$product_name = get_the_title( absint( $course_woocommerce_product_id ) );
576
						} // End If Statement
577
						echo '<a href="' . esc_url( get_edit_post_link( absint( $course_woocommerce_product_id ) ) ) . '" title="' . esc_attr( sprintf( __( 'Edit %s', 'woothemes-sensei' ), $product_name ) ) . '">' . $product_name . '</a>';
578
					} // End If Statement
579
				} // End If Statement
580
			break;
581
582
			case 'course-category':
583
				$output = get_the_term_list( $id, 'course-category', '', ', ', '' );
584
				if ( '' == $output ) {
585
					$output = __( 'None', 'woothemes-sensei' );
586
				} // End If Statement
587
				echo $output;
588
			break;
589
590
			default:
591
			break;
592
		}
593
	} // End add_column_data()
594
595
596
	/**
597
	 * course_query function.
598
	 *
599
	 * @access public
600
	 * @param int $amount (default: 0)
601
	 * @param string $type (default: 'default')
602
	 * @param array $includes (default: array())
603
	 * @return array
604
	 */
605
	public function course_query( $amount = 0, $type = 'default', $includes = array(), $excludes = array() ) {
606
		global $my_courses_page ;
607
608
		$results_array = array();
609
610
		if( $my_courses_page ) { add_action( 'pre_get_posts', array( $this, 'filter_my_courses' ) ); }
611
612
		$post_args = $this->get_archive_query_args( $type, $amount, $includes, $excludes );
613
614
		// get the posts
615
		if( empty( $post_args ) ) {
616
617
			return $results_array;
618
619
		}else{
620
621
			//reset the pagination as this widgets do not need it
622
			$post_args['paged'] = 1;
623
			$results_array = get_posts( $post_args );
624
625
		}
626
627
		if( $my_courses_page ) { remove_action( 'pre_get_posts', array( $this, 'filter_my_courses' ) ); }
628
629
		return $results_array;
630
631
	} // End course_query()
632
633
634
	/**
635
	 * get_archive_query_args function.
636
	 *
637
	 * @access public
638
	 * @param string $type (default: '')
639
	 * @param int $amount (default: 0)
640
	 * @param array $includes (default: array())
641
	 * @return array
642
	 */
643
	public function get_archive_query_args( $type = '', $amount = 0 , $includes = array(), $excludes = array() ) {
644
645
		global $wp_query;
646
647
		if ( 0 == $amount && ( isset( Sensei()->settings->settings[ 'course_archive_amount' ] ) && 'usercourses' != $type && ( 0 < absint( Sensei()->settings->settings[ 'course_archive_amount' ] ) ) ) ) {
648
			$amount = absint( Sensei()->settings->settings[ 'course_archive_amount' ] );
649
		} else {
650
			if ( 0 == $amount) {
651
				$amount = $wp_query->get( 'posts_per_page' );
652
			} // End If Statement
653
		} // End If Statement
654
655
        $stored_order = get_option( 'sensei_course_order', '' );
656
        $order = 'ASC';
657
        $orderby = 'menu_order';
658
        if( empty( $stored_order ) ){
659
660
            $order = 'DESC';
661
            $orderby = 'date';
662
663
        }
664
665
		switch ($type) {
666
667 View Code Duplication
			case 'usercourses':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
668
				$post_args = array(	'post_type' 		=> 'course',
669
									'orderby'         	=> $orderby,
670
    								'order'           	=> $order,
671
    								'post_status'      	=> 'publish',
672
    								'include'			=> $includes,
673
    								'exclude'			=> $excludes,
674
    								'suppress_filters' 	=> 0
675
									);
676
				break;
677 View Code Duplication
			case 'freecourses':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
678
679
                $post_args = array(
680
                    'post_type' 		=> 'course',
681
                    'orderby'         	=> $orderby,
682
                    'order'           	=> $order,
683
                    'post_status'      	=> 'publish',
684
                    'exclude'			=> $excludes,
685
                    'suppress_filters' 	=> 0
686
                );
687
                // Sub Query to get all WooCommerce Products that have Zero price
688
                $post_args['meta_query'] = Sensei_WC::get_free_courses_meta_query_args();
689
690
                break;
691
692 View Code Duplication
			case 'paidcourses':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
693
694
                $post_args = array(
695
                    'post_type' 		=> 'course',
696
                    'orderby'         	=> $orderby,
697
                    'order'           	=> $order,
698
                    'post_status'      	=> 'publish',
699
                    'exclude'			=> $excludes,
700
                    'suppress_filters' 	=> 0
701
                );
702
703
                // Sub Query to get all WooCommerce Products that have price greater than zero
704
                $post_args['meta_query'] = Sensei_WC::get_paid_courses_meta_query_args();
705
706
				break;
707
708
			case 'featuredcourses':
709
                $post_args = array(	'post_type' 		=> 'course',
710
                                    'orderby'         	=> $orderby,
711
                                    'order'           	=> $order,
712
    								'post_status'      	=> 'publish',
713
    								'meta_value' 		=> 'featured',
714
    								'meta_key' 			=> '_course_featured',
715
    								'meta_compare' 		=> '=',
716
    								'exclude'			=> $excludes,
717
    								'suppress_filters' 	=> 0
718
									);
719
				break;
720 View Code Duplication
			default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
721
				$post_args = array(	'post_type' 		=> 'course',
722
                                    'orderby'         	=> $orderby,
723
                                    'order'           	=> $order,
724
    								'post_status'      	=> 'publish',
725
    								'exclude'			=> $excludes,
726
    								'suppress_filters' 	=> 0
727
									);
728
				break;
729
730
		}
731
732
        $post_args['posts_per_page'] = $amount;
733
        $paged = $wp_query->get( 'paged' );
734
        $post_args['paged'] = empty( $paged) ? 1 : $paged;
735
736
        if( 'newcourses' == $type ){
737
738
            $post_args[ 'orderby' ] = 'date';
739
            $post_args[ 'order' ] = 'DESC';
740
        }
741
742
		return $post_args;
743
	}
744
745
746
	/**
747
	 * course_image function.
748
	 *
749
	 * Outputs the courses image, or first image from a lesson within a course
750
     *
751
     * Will echo the image unless return true is specified.
752
	 *
753
	 * @access public
754
	 * @param int | WP_Post $course_id (default: 0)
755
	 * @param string $width (default: '100')
756
	 * @param string $height (default: '100')
757
     * @param bool $return default false
758
     *
759
	 * @return string | void
760
	 */
761
	public function course_image( $course_id = 0, $width = '100', $height = '100', $return = false ) {
762
763
        if( is_a( $course_id, 'WP_Post' ) ){
764
            $course_id = $course_id->ID;
765
        }
766
767
		$html = '';
768
769
		// Get Width and Height settings
770
		if ( ( $width == '100' ) && ( $height == '100' ) ) {
771
772
			if ( is_singular( 'course' ) ) {
773
774
				if ( !Sensei()->settings->settings[ 'course_single_image_enable' ] ) {
775
					return '';
776
				} // End If Statement
777
				$image_thumb_size = 'course_single_image';
778
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
779
				$width = $dimensions['width'];
780
				$height = $dimensions['height'];
781
782
			} else {
783
784
				if ( !Sensei()->settings->settings[ 'course_archive_image_enable' ] ) {
785
					return '';
786
				} // End If Statement
787
788
				$image_thumb_size = 'course_archive_image';
789
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
790
				$width = $dimensions['width'];
791
				$height = $dimensions['height'];
792
793
			} // End If Statement
794
795
		} // End If Statement
796
797
		$img_url = '';
798
		if ( has_post_thumbnail( $course_id ) ) {
799
   			// Get Featured Image
800
   			$img_url = get_the_post_thumbnail( $course_id, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
801
 		} else {
802
803
			// Check for a Lesson Image
804
			$course_lessons = $this->course_lessons( $course_id );
805
806
			foreach ($course_lessons as $lesson_item){
807
				if ( has_post_thumbnail( $lesson_item->ID ) ) {
808
					// Get Featured Image
809
					$img_url = get_the_post_thumbnail( $lesson_item->ID, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
810
					if ( '' != $img_url ) {
811
						break;
812
					} // End If Statement
813
814
				} // End If Statement
815
			} // End For Loop
816
817 View Code Duplication
 			if ( '' == $img_url ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
818
819
 				// Display Image Placeholder if none
820
				if ( Sensei()->settings->get( 'placeholder_images_enable' ) ) {
821
822
                    $img_url = apply_filters( 'sensei_course_placeholder_image_url', '<img src="http://placehold.it/' . $width . 'x' . $height . '" class="woo-image thumbnail alignleft" />' );
823
824
				} // End If Statement
825
826
 			} // End If Statement
827
828
		} // End If Statement
829
830
		if ( '' != $img_url ) {
831
832
			$html .= '<a href="' . get_permalink( $course_id ) . '" title="' . esc_attr( get_post_field( 'post_title', $course_id ) ) . '">' . $img_url  .'</a>';
833
834
		} // End If Statement
835
836
        if( $return ){
837
838
            return $html;
839
840
        }else{
841
842
            echo $html;
843
844
        }
845
846
	} // End course_image()
847
848
849
	/**
850
	 * course_count function.
851
	 *
852
	 * @access public
853
	 * @param array $exclude (default: array())
0 ignored issues
show
Bug introduced by
There is no parameter named $exclude. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
854
	 * @param string $post_status (default: 'publish')
855
	 * @return int
856
	 */
857
	public function course_count( $post_status = 'publish' ) {
858
859
		$post_args = array(	'post_type'         => 'course',
860
							'posts_per_page'    => -1,
861
//							'orderby'           => 'menu_order date',
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
862
//							'order'             => 'ASC',
863
							'post_status'       => $post_status,
864
							'suppress_filters'  => 0,
865
							'fields'            => 'ids',
866
							);
867
868
		// Allow WP to generate the complex final query, just shortcut to only do an overall count
869
//		add_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
870
		$courses_query = new WP_Query( apply_filters( 'sensei_course_count', $post_args ) );
871
//		remove_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
872
873
		return count( $courses_query->posts );
874
	} // End course_count()
875
876
877
	/**
878
	 * course_lessons function.
879
	 *
880
	 * @access public
881
	 * @param int $course_id (default: 0)
882
	 * @param string $post_status (default: 'publish')
883
	 * @param string $fields (default: 'all'). WP only allows 3 types, but we will limit it to only 'ids' or 'all'
884
	 * @return array{ type WP_Post }  $posts_array
0 ignored issues
show
Documentation introduced by
The doc-type array{ could not be parsed: Unknown type name "array{" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
885
	 */
886
	public function course_lessons( $course_id = 0, $post_status = 'publish', $fields = 'all' ) {
887
888
        if( is_a( $course_id, 'WP_Post' ) ){
889
            $course_id = $course_id->ID;
890
        }
891
892
		$post_args = array(	'post_type'         => 'lesson',
893
							'posts_per_page'       => -1,
894
							'orderby'           => 'date',
895
							'order'             => 'ASC',
896
							'meta_query'        => array(
897
								array(
898
									'key' => '_lesson_course',
899
									'value' => intval( $course_id ),
900
								),
901
							),
902
							'post_status'       => $post_status,
903
							'suppress_filters'  => 0,
904
							);
905
		$query_results = new WP_Query( $post_args );
906
        $lessons = $query_results->posts;
907
908
        // re order the lessons. This could not be done via the OR meta query as there may be lessons
909
        // with the course order for a different course and this should not be included. It could also not
910
        // be done via the AND meta query as it excludes lesson that does not have the _order_$course_id but
911
        // that have been added to the course.
912
        if( count( $lessons) > 1  ){
913
914
            foreach( $lessons as $lesson ){
915
916
                $order = intval( get_post_meta( $lesson->ID, '_order_'. $course_id, true ) );
917
                // for lessons with no order set it to be 10000 so that it show up at the end
918
                $lesson->course_order = $order ? $order : 100000;
919
            }
920
921
            uasort( $lessons, array( $this, '_short_course_lessons_callback' )   );
922
        }
923
924
        /**
925
         * Filter runs inside Sensei_Course::course_lessons function
926
         *
927
         * Returns all lessons for a given course
928
         *
929
         * @param array $lessons
930
         * @param int $course_id
931
         */
932
        $lessons = apply_filters( 'sensei_course_get_lessons', $lessons, $course_id  );
933
934
        //return the requested fields
935
        // runs after the sensei_course_get_lessons filter so the filter always give an array of lesson
936
        // objects
937
        if( 'ids' == $fields ) {
938
            $lesson_objects = $lessons;
939
            $lessons = array();
940
941
            foreach ($lesson_objects as $lesson) {
942
                $lessons[] = $lesson->ID;
943
            }
944
        }
945
946
        return $lessons;
947
948
	} // End course_lessons()
949
950
    /**
951
     * Used for the uasort in $this->course_lessons()
952
     * @since 1.8.0
953
     * @access protected
954
     *
955
     * @param array $lesson_1
956
     * @param array $lesson_2
957
     * @return int
958
     */
959
    protected function _short_course_lessons_callback( $lesson_1, $lesson_2 ){
960
961
        if ( $lesson_1->course_order == $lesson_2->course_order ) {
962
            return 0;
963
        }
964
965
        return ($lesson_1->course_order < $lesson_2->course_order) ? -1 : 1;
966
    }
967
968
	/**
969
	 * Fetch all quiz ids in a course
970
	 * @since  1.5.0
971
	 * @param  integer $course_id ID of course
972
	 * @param  boolean $boolean_check True if a simple yes/no is required
973
	 * @return array              Array of quiz post objects
974
	 */
975
	public function course_quizzes( $course_id = 0, $boolean_check = false ) {
976
977
978
		$course_quizzes = array();
979
980
		if( $course_id ) {
981
			$lesson_ids = Sensei()->course->course_lessons( $course_id, 'any', 'ids' );
982
983
			foreach( $lesson_ids as $lesson_id ) {
984
				$has_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
985
				if ( $has_questions && $boolean_check ) {
986
					return true;
987
				}
988
				elseif ( $has_questions ) {
989
					$quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
990
//					$questions = Sensei()->lesson->lesson_quiz_questions( $quiz_id );
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
991
//					if( count( $questions ) > 0 ) {
992
						$course_quizzes[] = $quiz_id;
993
//					}
994
				}
995
			}
996
		}
997
		if ( $boolean_check && empty($course_quizzes) ) {
998
			$course_quizzes = false;
999
		}
1000
		return $course_quizzes;
1001
	}
1002
1003
1004
	/**
1005
	 * course_lessons_completed function. Appears to be completely unused and a duplicate of course_lessons()!
1006
	 *
1007
	 * @access public
1008
	 * @param  int $course_id (default: 0)
1009
	 * @param  string $post_status (default: 'publish')
1010
	 * @return array
1011
	 */
1012
	public function course_lessons_completed( $course_id = 0, $post_status = 'publish' ) {
1013
1014
		return $this->course_lessons( $course_id, $post_status );
1015
1016
	} // End course_lessons_completed()
1017
1018
1019
	/**
1020
	 * course_author_lesson_count function.
1021
	 *
1022
	 * @access public
1023
	 * @param  int $author_id (default: 0)
1024
	 * @param  int $course_id (default: 0)
1025
	 * @return int
1026
	 */
1027 View Code Duplication
	public function course_author_lesson_count( $author_id = 0, $course_id = 0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1028
1029
        $lesson_args = array(	'post_type' 		=> 'lesson',
1030
								'posts_per_page' 		=> -1,
1031
		    					'author'         	=> $author_id,
1032
		    					'meta_key'        	=> '_lesson_course',
1033
    							'meta_value'      	=> $course_id,
1034
    	    					'post_status'      	=> 'publish',
1035
    	    					'suppress_filters' 	=> 0,
1036
								'fields'            => 'ids', // less data to retrieve
1037
		    				);
1038
		$lessons_array = get_posts( $lesson_args );
1039
		$count = count( $lessons_array );
1040
		return $count;
1041
1042
	} // End course_author_lesson_count()
1043
1044
	/**
1045
	 * course_lesson_count function.
1046
	 *
1047
	 * @access public
1048
	 * @param  int $course_id (default: 0)
1049
	 * @return int
1050
	 */
1051 View Code Duplication
	public function course_lesson_count( $course_id = 0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1052
1053
		$lesson_args = array(	'post_type' 		=> 'lesson',
1054
								'posts_per_page' 		=> -1,
1055
		    					'meta_key'        	=> '_lesson_course',
1056
    							'meta_value'      	=> $course_id,
1057
    	    					'post_status'      	=> 'publish',
1058
    	    					'suppress_filters' 	=> 0,
1059
								'fields'            => 'ids', // less data to retrieve
1060
		    				);
1061
		$lessons_array = get_posts( $lesson_args );
1062
1063
        $count = count( $lessons_array );
1064
1065
        return $count;
1066
1067
	} // End course_lesson_count()
1068
1069
	/**
1070
	 * course_lesson_preview_count function.
1071
	 *
1072
	 * @access public
1073
	 * @param  int $course_id (default: 0)
1074
	 * @return int
1075
	 */
1076 View Code Duplication
	public function course_lesson_preview_count( $course_id = 0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1077
1078
		$lesson_args = array(	'post_type' 		=> 'lesson',
1079
								'posts_per_page' 		=> -1,
1080
    	    					'post_status'      	=> 'publish',
1081
    	    					'suppress_filters' 	=> 0,
1082
    	    					'meta_query' => array(
1083
									array(
1084
										'key' => '_lesson_course',
1085
										'value' => $course_id
1086
									),
1087
									array(
1088
										'key' => '_lesson_preview',
1089
										'value' => 'preview'
1090
									)
1091
								),
1092
								'fields'            => 'ids', // less data to retrieve
1093
		    				);
1094
		$lessons_array = get_posts( $lesson_args );
1095
1096
		$count = count( $lessons_array );
1097
1098
        return $count;
1099
1100
	} // End course_lesson_count()
1101
1102
	/**
1103
	 * get_product_courses function.
1104
	 *
1105
	 * @access public
1106
	 * @param  int $product_id (default: 0)
1107
	 * @return array
1108
	 */
1109 View Code Duplication
	public function get_product_courses( $product_id = 0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1110
1111
		$posts_array = array();
1112
		// Check for WooCommerce
1113
		if ( Sensei_WC::is_woocommerce_active() && 0 < $product_id ) {
1114
			$post_args = array(	'post_type' 		=> 'course',
1115
								'posts_per_page' 		=> -1,
1116
								'meta_key'        	=> '_course_woocommerce_product',
1117
	    						'meta_value'      	=> $product_id,
1118
	    						'post_status'       => 'publish',
1119
								'suppress_filters' 	=> 0,
1120
								'orderby' 			=> 'menu_order date',
1121
								'order' 			=> 'ASC',
1122
								);
1123
			$posts_array = get_posts( $post_args );
1124
		} // End If Statement
1125
		return $posts_array;
1126
1127
	} // End get_product_courses()
1128
1129
	/**
1130
	 * Fix posts_per_page for My Courses page
1131
	 * @param  WP_Query $query
1132
	 * @return void
1133
	 */
1134
	public function filter_my_courses( $query ) {
1135
		global  $my_courses_section;
1136
1137 View Code Duplication
		if ( isset( Sensei()->settings->settings[ 'my_course_amount' ] ) && ( 0 < absint( Sensei()->settings->settings[ 'my_course_amount' ] ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1138
			$amount = absint( Sensei()->settings->settings[ 'my_course_amount' ] );
1139
			$query->set( 'posts_per_page', $amount );
1140
		}
1141
1142
		if( isset( $_GET[ $my_courses_section . '_page' ] ) && 0 < intval( $_GET[ $my_courses_section . '_page' ] ) ) {
1143
			$page = intval( $_GET[ $my_courses_section . '_page' ] );
1144
			$query->set( 'paged', $page );
1145
		}
1146
	}
1147
1148
	/**
1149
	 * load_user_courses_content generates HTML for user's active & completed courses
1150
     *
1151
     * This function also ouputs the html so no need to echo the content.
1152
     *
1153
	 * @since  1.4.0
1154
	 * @param  object  $user   Queried user object
1155
	 * @param  boolean $manage Whether the user has permission to manage the courses
0 ignored issues
show
Bug introduced by
There is no parameter named $manage. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1156
	 * @return string          HTML displayng course data
1157
	 */
1158
	public function load_user_courses_content( $user = false ) {
1159
		global $course, $my_courses_page, $my_courses_section;
1160
1161
        if( ! isset( Sensei()->settings->settings[ 'learner_profile_show_courses' ] )
1162
            || ! Sensei()->settings->settings[ 'learner_profile_show_courses' ] ) {
1163
1164
            // do not show the content if the settings doesn't allow for it
1165
            return;
1166
1167
        }
1168
1169
        $manage = ( $user->ID == get_current_user_id() ) ? true : false;
1170
1171
        do_action( 'sensei_before_learner_course_content', $user );
1172
1173
		// Build Output HTML
1174
		$complete_html = $active_html = '';
1175
1176
		if( is_a( $user, 'WP_User' ) ) {
1177
1178
			$my_courses_page = true;
1179
1180
			// Allow action to be run before My Courses content has loaded
1181
			do_action( 'sensei_before_my_courses', $user->ID );
1182
1183
			// Logic for Active and Completed Courses
1184
			$per_page = 20;
1185
			if ( isset( Sensei()->settings->settings[ 'my_course_amount' ] )
1186
                && ( 0 < absint( Sensei()->settings->settings[ 'my_course_amount' ] ) ) ) {
1187
1188
				$per_page = absint( Sensei()->settings->settings[ 'my_course_amount' ] );
1189
1190
			}
1191
1192
			$course_statuses = Sensei_Utils::sensei_check_for_activity( array( 'user_id' => $user->ID, 'type' => 'sensei_course_status' ), true );
1193
			// User may only be on 1 Course
1194
			if ( !is_array($course_statuses) ) {
1195
				$course_statuses = array( $course_statuses );
1196
			}
1197
			$completed_ids = $active_ids = array();
1198 View Code Duplication
			foreach( $course_statuses as $course_status ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1199
				if ( Sensei_Utils::user_completed_course( $course_status, $user->ID ) ) {
1200
					$completed_ids[] = $course_status->comment_post_ID;
1201
				} else {
1202
					$active_ids[] = $course_status->comment_post_ID;
1203
				}
1204
			}
1205
1206
			$active_count = $completed_count = 0;
1207
1208
			$active_courses = array();
1209 View Code Duplication
			if ( 0 < intval( count( $active_ids ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1210
				$my_courses_section = 'active';
1211
				$active_courses = Sensei()->course->course_query( $per_page, 'usercourses', $active_ids );
1212
				$active_count = count( $active_ids );
1213
			} // End If Statement
1214
1215
			$completed_courses = array();
1216 View Code Duplication
			if ( 0 < intval( count( $completed_ids ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1217
				$my_courses_section = 'completed';
1218
				$completed_courses = Sensei()->course->course_query( $per_page, 'usercourses', $completed_ids );
1219
				$completed_count = count( $completed_ids );
1220
			} // End If Statement
1221
1222
			foreach ( $active_courses as $course_item ) {
1223
1224
				$course_lessons =  Sensei()->course->course_lessons( $course_item->ID );
1225
				$lessons_completed = 0;
1226
				foreach ( $course_lessons as $lesson ) {
1227
					if ( Sensei_Utils::user_completed_lesson( $lesson->ID, $user->ID ) ) {
1228
						++$lessons_completed;
1229
					}
1230
				}
1231
1232
			    // Get Course Categories
1233
			    $category_output = get_the_term_list( $course_item->ID, 'course-category', '', ', ', '' );
1234
1235
                $active_html .= '<article class="' . esc_attr( join( ' ', get_post_class( array( 'course', 'post' ), $course_item->ID ) ) ) . '">';
1236
1237
                // Image
1238
                $active_html .= Sensei()->course->course_image( absint( $course_item->ID ), '100','100', true );
1239
1240
                // Title
1241
                $active_html .= '<header>';
1242
1243
                $active_html .= '<h2><a href="' . esc_url( get_permalink( absint( $course_item->ID ) ) ) . '" title="' . esc_attr( $course_item->post_title ) . '">' . esc_html( $course_item->post_title ) . '</a></h2>';
1244
1245
                $active_html .= '</header>';
1246
1247
                $active_html .= '<section class="entry">';
1248
1249
                $active_html .= '<p class="sensei-course-meta">';
1250
1251
                // Author
1252
                $user_info = get_userdata( absint( $course_item->post_author ) );
1253 View Code Duplication
                if ( isset( Sensei()->settings->settings[ 'course_author' ] )
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1254
                    && ( Sensei()->settings->settings[ 'course_author' ] ) ) {
1255
1256
                    $active_html .= '<span class="course-author">'
1257
                        . __( 'by ', 'woothemes-sensei' )
1258
                        . '<a href="' . esc_url( get_author_posts_url( absint( $course_item->post_author ) ) )
1259
                        . '" title="' . esc_attr( $user_info->display_name ) . '">'
1260
                        . esc_html( $user_info->display_name )
1261
                        . '</a></span>';
1262
1263
                } // End If Statement
1264
1265
                // Lesson count for this author
1266
                $lesson_count = Sensei()->course->course_lesson_count( absint( $course_item->ID ) );
1267
                // Handle Division by Zero
1268
                if ( 0 == $lesson_count ) {
1269
1270
                    $lesson_count = 1;
1271
1272
                } // End If Statement
1273
                $active_html .= '<span class="course-lesson-count">' . $lesson_count . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' ) . '</span>';
1274
                // Course Categories
1275
                if ( '' != $category_output ) {
1276
1277
                    $active_html .= '<span class="course-category">' . sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ) . '</span>';
1278
1279
                } // End If Statement
1280
                $active_html .= '<span class="course-lesson-progress">' . sprintf( __( '%1$d of %2$d lessons completed', 'woothemes-sensei' ) , $lessons_completed, $lesson_count  ) . '</span>';
1281
1282
                $active_html .= '</p>';
1283
1284
                $active_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>';
1285
1286
1287
1288
                $progress_percentage = abs( round( ( doubleval( $lessons_completed ) * 100 ) / ( $lesson_count ), 0 ) );
1289
1290
                $active_html .= $this->get_progress_meter( $progress_percentage );
1291
1292
                $active_html .= '</section>';
1293
1294
                if( is_user_logged_in() ) {
1295
1296
                    $active_html .= '<section class="entry-actions">';
1297
1298
                    $active_html .= '<form method="POST" action="' . esc_url( remove_query_arg( array( 'active_page', 'completed_page' ) ) ) . '">';
1299
1300
                    $active_html .= '<input type="hidden" name="' . esc_attr( 'woothemes_sensei_complete_course_noonce' ) . '" id="' . esc_attr( 'woothemes_sensei_complete_course_noonce' ) . '" value="' . esc_attr( wp_create_nonce( 'woothemes_sensei_complete_course_noonce' ) ) . '" />';
1301
1302
                    $active_html .= '<input type="hidden" name="course_complete_id" id="course-complete-id" value="' . esc_attr( absint( $course_item->ID ) ) . '" />';
1303
1304
                    if ( 0 < absint( count( $course_lessons ) ) && Sensei()->settings->settings['course_completion'] == 'complete' ) {
1305
1306
                        $active_html .= '<span><input name="course_complete" type="submit" class="course-complete" value="'
1307
                            .  __( 'Mark as Complete', 'woothemes-sensei' ) . '"/> </span>';
1308
1309
                    } // End If Statement
1310
1311
                    $course_purchased = false;
1312
                    if ( Sensei_WC::is_woocommerce_active() ) {
1313
1314
                        // Get the product ID
1315
                        $wc_post_id = get_post_meta( absint( $course_item->ID ), '_course_woocommerce_product', true );
1316
                        if ( 0 < $wc_post_id ) {
1317
1318
                            $course_purchased = Sensei_WC::has_customer_bought_product(  $user->ID, $wc_post_id );
1319
1320
                        } // End If Statement
1321
1322
                    } // End If Statement
1323
1324
                    if ( false == $course_purchased ) {
1325
1326
                        $active_html .= '<span><input name="course_complete" type="submit" class="course-delete" value="'
1327
                            .  __( 'Delete Course', 'woothemes-sensei' ) . '"/></span>';
1328
1329
                    } // End If Statement
1330
1331
                    $active_html .= '</form>';
1332
1333
                    $active_html .= '</section>';
1334
                }
1335
1336
                $active_html .= '</article>';
1337
			}
1338
1339
			// Active pagination
1340 View Code Duplication
			if( $active_count > $per_page ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1341
1342
				$current_page = 1;
1343
				if( isset( $_GET['active_page'] ) && 0 < intval( $_GET['active_page'] ) ) {
1344
					$current_page = $_GET['active_page'];
1345
				}
1346
1347
				$active_html .= '<nav class="pagination woo-pagination">';
1348
				$total_pages = ceil( $active_count / $per_page );
1349
1350
				if( $current_page > 1 ) {
1351
					$prev_link = add_query_arg( 'active_page', $current_page - 1 );
1352
					$active_html .= '<a class="prev page-numbers" href="' . esc_url( $prev_link ) . '">' . __( 'Previous' , 'woothemes-sensei' ) . '</a> ';
1353
				}
1354
1355
				for ( $i = 1; $i <= $total_pages; $i++ ) {
1356
					$link = add_query_arg( 'active_page', $i );
1357
1358
					if( $i == $current_page ) {
1359
						$active_html .= '<span class="page-numbers current">' . $i . '</span> ';
1360
					} else {
1361
						$active_html .= '<a class="page-numbers" href="' . esc_url( $link ). '">' . $i . '</a> ';
1362
					}
1363
				}
1364
1365
				if( $current_page < $total_pages ) {
1366
					$next_link = add_query_arg( 'active_page', $current_page + 1 );
1367
					$active_html .= '<a class="next page-numbers" href="' . esc_url( $next_link ) . '">' . __( 'Next' , 'woothemes-sensei' ) . '</a> ';
1368
				}
1369
1370
				$active_html .= '</nav>';
1371
			}
1372
1373
			foreach ( $completed_courses as $course_item ) {
1374
				$course = $course_item;
1375
1376
			    // Get Course Categories
1377
			    $category_output = get_the_term_list( $course_item->ID, 'course-category', '', ', ', '' );
1378
1379
		    	$complete_html .= '<article class="' . join( ' ', get_post_class( array( 'course', 'post' ), $course_item->ID ) ) . '">';
1380
1381
		    	    // Image
1382
		    		$complete_html .= Sensei()->course->course_image( absint( $course_item->ID ),100, 100, true );
1383
1384
		    		// Title
1385
		    		$complete_html .= '<header>';
1386
1387
		    		    $complete_html .= '<h2><a href="' . esc_url( get_permalink( absint( $course_item->ID ) ) ) . '" title="' . esc_attr( $course_item->post_title ) . '">' . esc_html( $course_item->post_title ) . '</a></h2>';
1388
1389
		    		$complete_html .= '</header>';
1390
1391
		    		$complete_html .= '<section class="entry">';
1392
1393
		    			$complete_html .= '<p class="sensei-course-meta">';
1394
1395
		    		    	// Author
1396
		    		    	$user_info = get_userdata( absint( $course_item->post_author ) );
1397 View Code Duplication
		    		    	if ( isset( Sensei()->settings->settings[ 'course_author' ] ) && ( Sensei()->settings->settings[ 'course_author' ] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1398
		    		    		$complete_html .= '<span class="course-author">' . __( 'by ', 'woothemes-sensei' ) . '<a href="' . esc_url( get_author_posts_url( absint( $course_item->post_author ) ) ) . '" title="' . esc_attr( $user_info->display_name ) . '">' . esc_html( $user_info->display_name ) . '</a></span>';
1399
		    		    	} // End If Statement
1400
1401
		    		    	// Lesson count for this author
1402
		    		    	$complete_html .= '<span class="course-lesson-count">'
1403
                                . Sensei()->course->course_lesson_count( absint( $course_item->ID ) )
1404
                                . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' )
1405
                                . '</span>';
1406
1407
		    		    	// Course Categories
1408
		    		    	if ( '' != $category_output ) {
1409
1410
		    		    		$complete_html .= '<span class="course-category">' . sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ) . '</span>';
1411
1412
		    		    	} // End If Statement
1413
1414
						$complete_html .= '</p>';
1415
1416
						$complete_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>';
1417
1418
                        $complete_html .= $this->get_progress_meter( 100 );
1419
1420
						if( $manage ) {
1421
							$has_quizzes = Sensei()->course->course_quizzes( $course_item->ID, true );
1422
							// Output only if there is content to display
1423
							if ( has_filter( 'sensei_results_links' ) || $has_quizzes ) {
1424
1425
1426
								$complete_html .= '<p class="sensei-results-links">';
1427
								$results_link = '';
1428 View Code Duplication
								if( $has_quizzes ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1429
1430
									$results_link = '<a class="button view-results" href="'
1431
                                        . Sensei()->course_results->get_permalink( $course_item->ID )
1432
                                        . '">' . __( 'View results', 'woothemes-sensei' )
1433
                                        . '</a>';
1434
								}
1435
								$complete_html .= apply_filters( 'sensei_results_links', $results_link );
1436
								$complete_html .= '</p>';
1437
1438
							}
1439
						}
1440
1441
		    		$complete_html .= '</section>';
1442
1443
		    	$complete_html .= '</article>';
1444
			}
1445
1446
			// Active pagination
1447 View Code Duplication
			if( $completed_count > $per_page ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1448
1449
				$current_page = 1;
1450
				if( isset( $_GET['completed_page'] ) && 0 < intval( $_GET['completed_page'] ) ) {
1451
					$current_page = $_GET['completed_page'];
1452
				}
1453
1454
				$complete_html .= '<nav class="pagination woo-pagination">';
1455
				$total_pages = ceil( $completed_count / $per_page );
1456
1457
1458
				if( $current_page > 1 ) {
1459
					$prev_link = add_query_arg( 'completed_page', $current_page - 1 );
1460
					$complete_html .= '<a class="prev page-numbers" href="' . esc_url( $prev_link ) . '">' . __( 'Previous' , 'woothemes-sensei' ) . '</a> ';
1461
				}
1462
1463
				for ( $i = 1; $i <= $total_pages; $i++ ) {
1464
					$link = add_query_arg( 'completed_page', $i );
1465
1466
					if( $i == $current_page ) {
1467
						$complete_html .= '<span class="page-numbers current">' . $i . '</span> ';
1468
					} else {
1469
						$complete_html .= '<a class="page-numbers" href="' . esc_url( $link ) . '">' . $i . '</a> ';
1470
					}
1471
				}
1472
1473
				if( $current_page < $total_pages ) {
1474
					$next_link = add_query_arg( 'completed_page', $current_page + 1 );
1475
					$complete_html .= '<a class="next page-numbers" href="' . esc_url( $next_link ) . '">' . __( 'Next' , 'woothemes-sensei' ) . '</a> ';
1476
				}
1477
1478
				$complete_html .= '</nav>';
1479
			}
1480
1481
		} // End If Statement
1482
1483
		if( $manage ) {
1484
			$no_active_message = __( 'You have no active courses.', 'woothemes-sensei' );
1485
			$no_complete_message = __( 'You have not completed any courses yet.', 'woothemes-sensei' );
1486
		} else {
1487
			$no_active_message =  __( 'This learner has no active courses.', 'woothemes-sensei' );
1488
			$no_complete_message =  __( 'This learner has not completed any courses yet.', 'woothemes-sensei' );
1489
		}
1490
1491
		ob_start();
1492
		?>
1493
1494
		<?php do_action( 'sensei_before_user_courses' ); ?>
1495
1496
		<?php
1497
		if( $manage && ( ! isset( Sensei()->settings->settings['messages_disable'] ) || ! Sensei()->settings->settings['messages_disable'] ) ) {
1498
			?>
1499
			<p class="my-messages-link-container">
1500
                <a class="my-messages-link" href="<?php echo get_post_type_archive_link( 'sensei_message' ); ?>"
1501
                   title="<?php _e( 'View & reply to private messages sent to your course & lesson teachers.', 'woothemes-sensei' ); ?>">
1502
                    <?php _e( 'My Messages', 'woothemes-sensei' ); ?>
1503
                </a>
1504
            </p>
1505
			<?php
1506
		}
1507
		?>
1508
		<div id="my-courses">
1509
1510
		    <ul>
1511
		    	<li><a href="#active-courses"><?php  _e( 'Active Courses', 'woothemes-sensei' ); ?></a></li>
1512
		    	<li><a href="#completed-courses"><?php  _e( 'Completed Courses', 'woothemes-sensei' ); ?></a></li>
1513
		    </ul>
1514
1515
		    <?php do_action( 'sensei_before_active_user_courses' ); ?>
1516
1517
		    <?php
1518
            $course_page_url = Sensei_Course::get_courses_page_url();
1519
            ?>
1520
1521
		    <div id="active-courses">
1522
1523
		    	<?php if ( '' != $active_html ) {
1524
1525
		    		echo $active_html;
1526
1527
		    	} else { ?>
1528
1529
		    		<div class="sensei-message info">
1530
1531
                        <?php echo $no_active_message; ?>
1532
1533
                        <a href="<?php echo $course_page_url; ?>">
1534
1535
                            <?php  _e( 'Start a Course!', 'woothemes-sensei' ); ?>
1536
1537
                        </a>
1538
1539
                    </div>
1540
1541
		    	<?php } // End If Statement ?>
1542
1543
		    </div>
1544
1545
		    <?php do_action( 'sensei_after_active_user_courses' ); ?>
1546
1547
		    <?php do_action( 'sensei_before_completed_user_courses' ); ?>
1548
1549
		    <div id="completed-courses">
1550
1551
		    	<?php if ( '' != $complete_html ) {
1552
1553
		    		echo $complete_html;
1554
1555
		    	} else { ?>
1556
1557
		    		<div class="sensei-message info">
1558
1559
                        <?php echo $no_complete_message; ?>
1560
1561
                    </div>
1562
1563
		    	<?php } // End If Statement ?>
1564
1565
		    </div>
1566
1567
		    <?php do_action( 'sensei_after_completed_user_courses' ); ?>
1568
1569
		</div>
1570
1571
		<?php do_action( 'sensei_after_user_courses' ); ?>
1572
1573
		<?php
1574
        echo ob_get_clean();
1575
1576
        do_action( 'sensei_after_learner_course_content', $user );
1577
1578
	} // end load_user_courses_content
1579
1580
    /**
1581
     * Returns a list of all courses
1582
     *
1583
     * @since 1.8.0
1584
     * @return array $courses{
1585
     *  @type $course WP_Post
1586
     * }
1587
     */
1588
    public static function get_all_courses(){
1589
1590
        $args = array(
1591
               'post_type' => 'course',
1592
                'posts_per_page' 		=> -1,
1593
                'orderby'         	=> 'title',
1594
                'order'           	=> 'ASC',
1595
                'post_status'      	=> 'any',
1596
                'suppress_filters' 	=> 0,
1597
        );
1598
1599
        $wp_query_obj =  new WP_Query( $args );
1600
1601
        /**
1602
         * sensei_get_all_courses filter
1603
         *
1604
         * This filter runs inside Sensei_Course::get_all_courses.
1605
         *
1606
         * @param array $courses{
1607
         *  @type WP_Post
1608
         * }
1609
         * @param array $attributes
1610
         */
1611
        return apply_filters( 'sensei_get_all_courses' , $wp_query_obj->posts );
1612
1613
    }// end get_all_courses
1614
1615
    /**
1616
     * Generate the course meter component
1617
     *
1618
     * @since 1.8.0
1619
     * @param int $progress_percentage 0 - 100
1620
     * @return string $progress_bar_html
1621
     */
1622
    public function get_progress_meter( $progress_percentage ){
1623
1624
        if ( 50 < $progress_percentage ) {
1625
            $class = ' green';
1626
        } elseif ( 25 <= $progress_percentage && 50 >= $progress_percentage ) {
1627
            $class = ' orange';
1628
        } else {
1629
            $class = ' red';
1630
        }
1631
        $progress_bar_html = '<div class="meter' . esc_attr( $class ) . '"><span style="width: ' . $progress_percentage . '%">' . round( $progress_percentage ) . '%</span></div>';
1632
1633
        return $progress_bar_html;
1634
1635
    }// end get_progress_meter
1636
1637
    /**
1638
     * Generate a statement that tells users
1639
     * how far they are in the course.
1640
     *
1641
     * @param int $course_id
1642
     * @param int $user_id
1643
     *
1644
     * @return string $statement_html
1645
     */
1646
    public function get_progress_statement( $course_id, $user_id ){
1647
1648
        if( empty( $course_id ) || empty( $user_id )
1649
        || ! Sensei_Utils::user_started_course( $course_id, $user_id ) ){
0 ignored issues
show
Bug Best Practice introduced by
The expression \Sensei_Utils::user_star...e($course_id, $user_id) of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1650
            return '';
1651
        }
1652
1653
        $completed = count( $this->get_completed_lesson_ids( $course_id, $user_id ) );
1654
        $total_lessons = count( $this->course_lessons( $course_id ) );
1655
1656
        $statement = sprintf( _n('Currently completed %s lesson of %s in total', 'Currently completed %s lessons of %s in total', $completed, 'woothemes-sensei'), $completed, $total_lessons );
1657
1658
        /**
1659
         * Filter the course completion statement.
1660
         * Default Currently completed $var lesson($plural) of $var in total
1661
         *
1662
         * @param string $statement
1663
         */
1664
        return apply_filters( 'sensei_course_completion_statement', $statement );
1665
1666
    }// end generate_progress_statement
1667
1668
    /**
1669
     * Output the course progress statement
1670
     *
1671
     * @param $course_id
1672
     * @return void
1673
     */
1674
    public function the_progress_statement( $course_id = 0, $user_id = 0 ){
1675
        if( empty( $course_id ) ){
1676
            global $post;
1677
            $course_id = $post->ID;
1678
        }
1679
1680
        if( empty( $user_id ) ){
1681
            $user_id = get_current_user_id();
1682
        }
1683
1684
        echo '<span class="progress statement  course-completion-rate">' . $this->get_progress_statement( $course_id, $user_id  ) . '</span>';
1685
    }
1686
1687
    /**
1688
     * Output the course progress bar
1689
     *
1690
     * @param $course_id
1691
     * @return void
1692
     */
1693
    public function the_progress_meter( $course_id = 0, $user_id = 0 ){
1694
1695
        if( empty( $course_id ) ){
1696
            global $post;
1697
            $course_id = $post->ID;
1698
        }
1699
1700
        if( empty( $user_id ) ){
1701
            $user_id = get_current_user_id();
1702
        }
1703
1704
        if( 'course' != get_post_type( $course_id ) || ! get_userdata( $user_id )
1705
            || ! Sensei_Utils::user_started_course( $course_id ,$user_id ) ){
0 ignored issues
show
Bug Best Practice introduced by
The expression \Sensei_Utils::user_star...e($course_id, $user_id) of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1706
            return;
1707
        }
1708
        $percentage_completed = $this->get_completion_percentage( $course_id, $user_id );
1709
1710
        echo $this->get_progress_meter( $percentage_completed );
1711
1712
    }// end the_progress_meter
1713
1714
    /**
1715
     * Checks how many lessons are completed
1716
     *
1717
     * @since 1.8.0
1718
     *
1719
     * @param int $course_id
1720
     * @param int $user_id
1721
     * @return array $completed_lesson_ids
1722
     */
1723
    public function get_completed_lesson_ids( $course_id, $user_id = 0 ){
1724
1725
        if( !( intval( $user_id ) ) > 0 ){
1726
            $user_id = get_current_user_id();
1727
        }
1728
1729
        $completed_lesson_ids = array();
1730
1731
        $course_lessons = $this->course_lessons( $course_id );
1732
1733
        foreach( $course_lessons as $lesson ){
1734
1735
            $is_lesson_completed = Sensei_Utils::user_completed_lesson( $lesson->ID, $user_id );
1736
            if( $is_lesson_completed ){
1737
                $completed_lesson_ids[] = $lesson->ID;
1738
            }
1739
1740
        }
1741
1742
        return $completed_lesson_ids;
1743
1744
    }// end get_completed_lesson_ids
1745
1746
    /**
1747
     * Calculate the perceantage completed in the course
1748
     *
1749
     * @since 1.8.0
1750
     *
1751
     * @param int $course_id
1752
     * @param int $user_id
1753
     * @return int $percentage
1754
     */
1755
    public function get_completion_percentage( $course_id, $user_id = 0 ){
1756
1757
        if( !( intval( $user_id ) ) > 0 ){
1758
            $user_id = get_current_user_id();
1759
        }
1760
1761
        $completed = count( $this->get_completed_lesson_ids( $course_id, $user_id ) );
1762
1763
        if( ! (  $completed  > 0 ) ){
1764
            return 0;
1765
        }
1766
1767
        $total_lessons = count( $this->course_lessons( $course_id ) );
1768
        $percentage = $completed / $total_lessons * 100;
1769
1770
        /**
1771
         *
1772
         * Filter the percentage returned for a users course.
1773
         *
1774
         * @param $percentage
1775
         * @param $course_id
1776
         * @param $user_id
1777
         * @since 1.8.0
1778
         */
1779
        return apply_filters( 'sensei_course_completion_percentage', $percentage, $course_id, $user_id );
1780
1781
    }// end get_completed_lesson_ids
1782
1783
    /**
1784
     * Block email notifications for the specific courses
1785
     * that the user disabled the notifications.
1786
     *
1787
     * @since 1.8.0
1788
     * @param $should_send
1789
     * @return bool
1790
     */
1791
    public function block_notification_emails( $should_send ){
1792
        global $sensei_email_data;
1793
        $email = $sensei_email_data;
1794
1795
        $course_id = '';
1796
1797
        if( isset( $email['course_id'] ) ){
1798
1799
            $course_id = $email['course_id'];
1800
1801
        }elseif( isset( $email['lesson_id'] ) ){
1802
1803
            $course_id = Sensei()->lesson->get_course_id( $email['lesson_id'] );
1804
1805
        }elseif( isset( $email['quiz_id'] ) ){
1806
1807
            $lesson_id = Sensei()->quiz->get_lesson_id( $email['quiz_id'] );
1808
            $course_id = Sensei()->lesson->get_course_id( $lesson_id );
1809
1810
        }
1811
1812
        if( !empty( $course_id ) && 'course'== get_post_type( $course_id ) ) {
1813
1814
            $course_emails_disabled = get_post_meta($course_id, 'disable_notification', true);
1815
1816
            if ($course_emails_disabled) {
1817
1818
                return false;
1819
1820
            }
1821
1822
        }// end if
1823
1824
        return $should_send;
1825
    }// end block_notification_emails
1826
1827
    /**
1828
     * Render the course notification setting meta box
1829
     *
1830
     * @since 1.8.0
1831
     * @param $course
1832
     */
1833
    public function course_notification_meta_box_content( $course ){
1834
1835
        $checked = get_post_meta( $course->ID , 'disable_notification', true );
1836
1837
        // generate checked html
1838
        $checked_html = '';
1839
        if( $checked ){
1840
            $checked_html = 'checked="checked"';
1841
        }
1842
        wp_nonce_field( 'update-course-notification-setting','_sensei_course_notification' );
1843
1844
        echo '<input id="disable_sensei_course_notification" '.$checked_html .' type="checkbox" name="disable_sensei_course_notification" >';
1845
        echo '<label for="disable_sensei_course_notification">'.__('Disable notifications on this course ?', 'woothemes-sensei'). '</label>';
1846
1847
    }// end course_notification_meta_box_content
1848
1849
    /**
1850
     * Store the setting for the course notification setting.
1851
     *
1852
     * @hooked int save_post
1853
     * @since 1.8.0
1854
     *
1855
     * @param $course_id
1856
     */
1857
    public function save_course_notification_meta_box( $course_id ){
1858
1859
        if( !isset( $_POST['_sensei_course_notification']  )
1860
            || ! wp_verify_nonce( $_POST['_sensei_course_notification'], 'update-course-notification-setting' ) ){
1861
            return;
1862
        }
1863
1864
        if( isset( $_POST['disable_sensei_course_notification'] ) && 'on'== $_POST['disable_sensei_course_notification']  ) {
1865
            $new_val = true;
1866
        }else{
1867
            $new_val = false;
1868
        }
1869
1870
       update_post_meta( $course_id , 'disable_notification', $new_val );
1871
1872
    }// end save notification meta box
1873
1874
    /**
1875
     * Backwards compatibility hooks added to ensure that
1876
     * plugins and other parts of sensei still works.
1877
     *
1878
     * This function hooks into `sensei_course_content_inside_before`
1879
     *
1880
     * @since 1.9
1881
     *
1882
     * @param WP_Post $post
1883
     */
1884
    public function content_before_backwards_compatibility_hooks( $post ){
0 ignored issues
show
Unused Code introduced by
The parameter $post is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1885
1886
        sensei_do_deprecated_action( 'sensei_course_image','1.9.0','sensei_course_content_inside_before' );
1887
        sensei_do_deprecated_action( 'sensei_course_archive_course_title','1.9.0','sensei_course_content_inside_before' );
1888
1889
    }
1890
1891
    /**
1892
     * Backwards compatibility hooks that should be hooked into sensei_loop_course_before
1893
     *
1894
     * hooked into 'sensei_loop_course_before'
1895
     *
1896
     * @since 1.9
1897
     *
1898
     * @global WP_Post $post
1899
     */
1900
    public  function loop_before_backwards_compatibility_hooks( ){
1901
1902
        global $post;
1903
        sensei_do_deprecated_action( 'sensei_course_archive_header','1.9.0','sensei_course_content_inside_before', $post->post_type  );
1904
1905
    }
1906
1907
    /**
1908
     * Output a link to view course. The button text is different depending on the amount of preview lesson available.
1909
     *
1910
     * hooked into 'sensei_course_content_inside_after'
1911
     *
1912
     * @since 1.9.0
1913
     *
1914
     * @param integer $course_id
1915
     */
1916
    public function the_course_free_lesson_preview( $course_id ){
1917
        // Meta data
1918
        $course = get_post( $course_id );
1919
        $preview_lesson_count = intval( Sensei()->course->course_lesson_preview_count( $course->ID ) );
1920
        $is_user_taking_course = Sensei_Utils::user_started_course( $course->ID, get_current_user_id() );
1921
1922 View Code Duplication
        if ( 0 < $preview_lesson_count && !$is_user_taking_course ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_user_taking_course of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1923
            ?>
1924
            <p class="sensei-free-lessons">
1925
                <a href="<?php echo get_permalink(); ?>">
1926
                    <?php _e( 'Preview this course', 'woothemes-sensei' ) ?>
1927
                </a>
1928
                - <?php echo sprintf( __( '(%d preview lessons)', 'woothemes-sensei' ), $preview_lesson_count ) ; ?>
1929
            </p>
1930
1931
        <?php
1932
        }
1933
    }
1934
1935
    /**
1936
     * Add course mata to the course meta hook
1937
     *
1938
     * @since 1.9.0
1939
     * @param integer $course_id
1940
     */
1941
    public function the_course_meta( $course_id ){
1942
        echo '<p class="sensei-course-meta">';
1943
1944
        $course = get_post( $course_id );
1945
        $category_output = get_the_term_list( $course->ID, 'course-category', '', ', ', '' );
1946
        $author_display_name = get_the_author_meta( 'display_name', $course->post_author  );
1947
1948
        if ( isset( Sensei()->settings->settings[ 'course_author' ] ) && ( Sensei()->settings->settings[ 'course_author' ] ) ) {?>
1949
1950
            <span class="course-author"><?php _e( 'by ', 'woothemes-sensei' ); ?>
1951
1952
                <a href="<?php esc_attr_e( get_author_posts_url( $course->post_author ) ); ?>" title="<?php esc_attr_e( $author_display_name ); ?>"><?php esc_attr_e( $author_display_name   ); ?></a>
1953
1954
            </span>
1955
1956
        <?php } // End If Statement ?>
1957
1958
        <span class="course-lesson-count"><?php echo Sensei()->course->course_lesson_count( $course->ID ) . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' ); ?></span>
1959
1960
       <?php if ( '' != $category_output ) { ?>
1961
1962
            <span class="course-category"><?php echo sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ); ?></span>
1963
1964
        <?php } // End If Statement
1965
1966
        // number of completed lessons
1967
        if( Sensei_Utils::user_started_course( $course->ID,  get_current_user_id() )
0 ignored issues
show
Bug Best Practice introduced by
The expression \Sensei_Utils::user_star... get_current_user_id()) of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1968
            || Sensei_Utils::user_completed_course( $course->ID,  get_current_user_id() )  ){
1969
1970
            $completed = count( $this->get_completed_lesson_ids( $course->ID, get_current_user_id() ) );
1971
            $lesson_count = count( $this->course_lessons( $course->ID ) );
1972
            echo '<span class="course-lesson-progress">' . sprintf( __( '%1$d of %2$d lessons completed', 'woothemes-sensei' ) , $completed, $lesson_count  ) . '</span>';
1973
1974
        }
1975
1976
        sensei_simple_course_price( $course->ID );
1977
1978
        echo '</p>';
1979
    } // end the course meta
1980
1981
    /**
1982
     * Filter the classes attached to a post types for courses
1983
     * and add a status class for when the user is logged in.
1984
     *
1985
     * @param $classes
1986
     * @param $class
1987
     * @param $post_id
1988
     *
1989
     * @return array $classes
1990
     */
1991
    public static function add_course_user_status_class( $classes, $class, $course_id ){
1992
1993
        if( 'course' == get_post_type( $course_id )  &&  is_user_logged_in() ){
1994
1995
            if( Sensei_Utils::user_completed_course( $course_id, get_current_user_id() ) ){
1996
1997
                $classes[] = 'user-status-completed';
1998
1999
            }else{
2000
2001
                $classes[] = 'user-status-active';
2002
2003
            }
2004
2005
        }
2006
2007
        return $classes;
2008
2009
    }// end add_course_user_status_class
2010
2011
    /**
2012
     * Prints out the course action buttons links
2013
     *
2014
     * - complete course
2015
     * - delete course
2016
     *
2017
     * @param WP_Post $course
2018
     */
2019
    public static function the_course_action_buttons( $course ){
2020
2021
        if( is_user_logged_in() ) { ?>
2022
2023
            <section class="entry-actions">
2024
                <form method="POST" action="<?php  echo esc_url( remove_query_arg( array( 'active_page', 'completed_page' ) ) ); ?>">
2025
2026
                    <input type="hidden"
2027
                           name="<?php esc_attr_e( 'woothemes_sensei_complete_course_noonce' ) ?>"
2028
                           id="<?php  esc_attr_e( 'woothemes_sensei_complete_course_noonce' ); ?>"
2029
                           value="<?php esc_attr_e( wp_create_nonce( 'woothemes_sensei_complete_course_noonce' ) ); ?>"
2030
                        />
2031
2032
                    <input type="hidden" name="course_complete_id" id="course-complete-id" value="<?php esc_attr_e( intval( $course->ID ) ); ?>" />
2033
2034
                    <?php if ( 0 < absint( count( Sensei()->course->course_lessons( $course->ID ) ) ) && Sensei()->settings->settings['course_completion'] == 'complete' ) { ?>
2035
2036
                        <span><input name="course_complete" type="submit" class="course-complete" value="<?php  _e( 'Mark as Complete', 'woothemes-sensei' ); ?>" /></span>
2037
2038
                   <?php  } // End If Statement
2039
2040
                    $course_purchased = false;
2041
                    if ( Sensei_WC::is_woocommerce_active() ) {
2042
                        // Get the product ID
2043
                        $wc_post_id = get_post_meta( intval( $course->ID ), '_course_woocommerce_product', true );
2044
                        if ( 0 < $wc_post_id ) {
2045
2046
                            $user = wp_get_current_user();
2047
                            $course_purchased = Sensei_Utils::sensei_customer_bought_product( $user->user_email, $user->ID, $wc_post_id );
0 ignored issues
show
Deprecated Code introduced by
The method Sensei_Utils::sensei_customer_bought_product() has been deprecated with message: since 1.9.0 use Sensei_WC::has_customer_bought_product($user_id, $product_id)

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
2048
2049
                        } // End If Statement
2050
                    } // End If Statement
2051
2052
                    if ( ! $course_purchased && ! Sensei_Utils::user_completed_course( $course->ID, get_current_user_id() ) ) {?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $course_purchased of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
2053
2054
                        <span><input name="course_complete" type="submit" class="course-delete" value="<?php echo __( 'Delete Course', 'woothemes-sensei' ); ?>"/></span>
2055
2056
                    <?php } // End If Statement
2057
2058
                    $has_quizzes = Sensei()->course->course_quizzes( $course->ID, true );
2059
                    $results_link = '';
2060 View Code Duplication
                    if( $has_quizzes ){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2061
                        $results_link = '<a class="button view-results" href="' . Sensei()->course_results->get_permalink( $course->ID ) . '">' . __( 'View results', 'woothemes-sensei' ) . '</a>';
2062
                    }
2063
2064
                    // Output only if there is content to display
2065
                    if ( has_filter( 'sensei_results_links' ) || $has_quizzes ) { ?>
2066
2067
                        <p class="sensei-results-links">
2068
                            <?php echo apply_filters( 'sensei_results_links', $results_link ); ?>
2069
                        </p>
2070
2071
                    <?php } // end if has filter  ?>
2072
                </form>
2073
            </section>
2074
2075
        <?php  }// end if is user logged in
2076
2077
    }// end the_course_action_buttons
2078
2079
    /**
2080
     * This function alter the main query on the course archive page.
2081
     * This also gives Sensei specific filters that allows variables to be altered specifically on the course archive.
2082
     *
2083
     * This function targets only the course archives and the my courses page. Shortcodes can set their own
2084
     * query parameters via the arguments.
2085
     *
2086
     * This function is hooked into pre_get_posts filter
2087
     *
2088
     * @since 1.9.0
2089
     *
2090
     * @param WP_Query $query
2091
     * @return WP_Query $query
2092
     */
2093
    public static function course_query_filter( $query ){
2094
2095
        // exit early for no course queries and admin queries
2096
        if( is_admin( ) || 'course' != $query->get( 'post_type' ) ){
2097
            return $query;
2098
        }
2099
2100
        global $post; // used to get the current page id for my courses
2101
2102
        // for the course archive page
2103
        if( $query->is_main_query() && is_post_type_archive('course') )
2104
        {
2105
            /**
2106
             * sensei_archive_courses_per_page
2107
             *
2108
             * Sensei courses per page on the course
2109
             * archive
2110
             *
2111
             * @since 1.9.0
2112
             * @param integer $posts_per_page default 10
2113
             */
2114
            $query->set( 'posts_per_page', apply_filters( 'sensei_archive_courses_per_page', 10 ) );
2115
2116
        }
2117
        // for the my courses page
2118
        elseif( is_page() && Sensei()->settings->get( 'my_course_page' ) == $post->ID  )
2119
        {
2120
            /**
2121
             * sensei_my_courses_per_page
2122
             *
2123
             * Sensei courses per page on the my courses page
2124
             * as set in the settings
2125
             *
2126
             * @since 1.9.0
2127
             * @param integer $posts_per_page default 10
2128
             */
2129
            $query->set( 'posts_per_page', apply_filters( 'sensei_my_courses_per_page', 10 ) );
2130
2131
        }
2132
2133
        return $query;
2134
2135
    }// end course_query_filter
2136
2137
    /**
2138
     * Determine the class of the course loop
2139
     *
2140
     * This will output .first or .last and .course-item-number-x
2141
     *
2142
     * @return array $extra_classes
2143
     * @since 1.9.0
2144
     */
2145
    public static function get_course_loop_content_class ()
2146
    {
2147
2148
        global $sensei_course_loop;
2149
2150
2151
        if( !isset( $sensei_course_loop ) ){
2152
            $sensei_course_loop = array();
2153
        }
2154
2155
        if (!isset($sensei_course_loop['counter'])) {
2156
            $sensei_course_loop['counter'] = 0;
2157
        }
2158
2159
        if (!isset($sensei_course_loop['columns'])) {
2160
            $sensei_course_loop['columns'] = self::get_loop_number_of_columns();
2161
        }
2162
2163
        // increment the counter
2164
        $sensei_course_loop['counter']++;
2165
2166
        $extra_classes = array();
2167
        if( 0 == ( $sensei_course_loop['counter'] - 1 ) % $sensei_course_loop['columns'] || 1 == $sensei_course_loop['columns']  ){
2168
            $extra_classes[] = 'first';
2169
        }
2170
2171
        if( 0 == $sensei_course_loop['counter'] % $sensei_course_loop['columns']  ){
2172
            $extra_classes[] = 'last';
2173
        }
2174
2175
        // add the item number to the classes as well.
2176
        $extra_classes[] = 'loop-item-number-'. $sensei_course_loop['counter'];
2177
2178
        /**
2179
         * Filter the course loop class the fires in the  in get_course_loop_content_class function
2180
         * which is called from the course loop content-course.php
2181
         *
2182
         * @since 1.9.0
2183
         *
2184
         * @param array $extra_classes
2185
         * @param WP_Post $loop_current_course
2186
         */
2187
        return apply_filters( 'sensei_course_loop_content_class', $extra_classes ,get_post() );
2188
2189
    }// end get_course_loop_class
2190
2191
    /**
2192
     * Get the number of columns set for Sensei courses
2193
     *
2194
     * @since 1.9.0
2195
     * @return mixed|void
2196
     */
2197
    public static function get_loop_number_of_columns(){
2198
2199
        /**
2200
         * Filter the number of columns on the course archive page.
2201
         *
2202
         * @since 1.9.0
2203
         * @param int $number_of_columns default 1
2204
         */
2205
        return apply_filters('sensei_course_loop_number_of_columns', 1);
2206
2207
    }
2208
2209
    /**
2210
     * Output the course archive filter markup
2211
     *
2212
     * hooked into sensei_loop_course_before
2213
     *
2214
     * @since 1.9.0
2215
     * @param
2216
     */
2217
    public static function course_archive_sorting( $query ){
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2218
2219
        // don't show on category pages and other pages
2220
        if( ! is_archive(  'course ') || is_tax('course-category') ){
2221
            return;
2222
        }
2223
2224
        /**
2225
         * Filter the sensei archive course order by values
2226
         *
2227
         * @since 1.9.0
2228
         * @param array $options {
2229
         *  @type string $option_value
2230
         *  @type string $option_string
2231
         * }
2232
         */
2233
        $course_order_by_options = apply_filters( 'sensei_archive_course_order_by_options', array(
2234
            "newness"     => __( "Sort by newest first", "woothemes-sensei"),
2235
            "title"       => __( "Sort by title A-Z", "woothemes-sensei" ),
2236
        ));
2237
2238
        // setup the currently selected item
2239
        $selected = 'newness';
2240
        if( isset( $_GET['orderby'] ) ){
2241
2242
            $selected =  $_GET[ 'orderby' ];
2243
2244
        }
2245
2246
        ?>
2247
2248
        <form class="sensei-ordering" name="sensei-course-order" action="<?php echo esc_attr( Sensei_Utils::get_current_url() ) ; ?>" method="POST">
2249
            <select name="course-orderby" class="orderby">
2250
                <?php
2251
                foreach( $course_order_by_options as $value => $text ){
2252
2253
                    echo '<option value="'. $value . ' "' . selected( $selected, $value, false ) . '>'. $text. '</option>';
2254
2255
                }
2256
                ?>
2257
            </select>
2258
        </form>
2259
2260
    <?php
2261
    }// end course archive filters
2262
2263
    /**
2264
     * Output the course archive filter markup
2265
     *
2266
     * hooked into sensei_loop_course_before
2267
     *
2268
     * @since 1.9.0
2269
     * @param
2270
     */
2271
    public static function course_archive_filters( $query ){
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2272
2273
        // don't show on category pages
2274
        if( is_tax('course-category') ){
2275
            return;
2276
        }
2277
2278
        /**
2279
         * filter the course archive filter buttons
2280
         *
2281
         * @since 1.9.0
2282
         * @param array $filters{
2283
         *   @type array ( $id, $url , $title )
2284
         * }
2285
         *
2286
         */
2287
        $filters = apply_filters( 'sensei_archive_course_filter_by_options', array(
2288
            array( 'id' => 'all', 'url' => self::get_courses_page_url(), 'title'=> __( 'All', 'woothemes-sensei' ) ),
2289
            array( 'id' => 'featured', 'url' => add_query_arg( array( 'course_filter'=>'featured'), self::get_courses_page_url()  ), 'title'=> __( 'Featured', 'woothemes-sensei' ) ),
2290
        ));
2291
2292
2293
        ?>
2294
        <ul class="sensei-course-filters clearfix" >
2295
            <?php
2296
2297
            //determine the current active url
2298
            $current_url = Sensei_Utils::get_current_url();
2299
2300
            foreach( $filters as $filter ) {
2301
2302
                $active_class =  $current_url == $filter['url'] ? ' class="active" ' : '';
2303
2304
                echo '<li><a '. $active_class .' id="'. $filter['id'] .'" href="'. esc_url( $filter['url'] ).'" >'. $filter['title']  .'</a></li>';
2305
2306
            }
2307
            ?>
2308
2309
        </ul>
2310
2311
        <?php
2312
2313
    }
2314
2315
    /**
2316
     * if the featured link is clicked on the course archive page
2317
     * filter the courses returned to only show those featured
2318
     *
2319
     * Hooked into pre_get_posts
2320
     *
2321
     * @since 1.9.0
2322
     * @param WP_Query $query
2323
     * @return WP_Query $query
2324
     */
2325
    public static function course_archive_featured_filter( $query ){
2326
2327
        if( isset ( $_GET[ 'course_filter' ] ) && 'featured'== $_GET['course_filter'] && $query->is_main_query()  ){
2328
            //setup meta query for featured courses
2329
            $query->set( 'meta_value', 'featured'  );
2330
            $query->set( 'meta_key', '_course_featured'  );
2331
            $query->set( 'meta_compare', '='  );
2332
        }
2333
2334
        return $query;
2335
    }
2336
2337
    /**
2338
     * if the course order drop down is changed
2339
     *
2340
     * Hooked into pre_get_posts
2341
     *
2342
     * @since 1.9.0
2343
     * @param WP_Query $query
2344
     * @return WP_Query $query
2345
     */
2346
    public static function course_archive_order_by_title( $query ){
2347
2348
        if( isset ( $_POST[ 'course-orderby' ] ) && 'title '== $_POST['course-orderby']
2349
            && 'course'== $query->get('post_type') && $query->is_main_query()  ){
2350
            // setup the order by title for this query
2351
            $query->set( 'orderby', 'title'  );
2352
            $query->set( 'order', 'ASC'  );
2353
        }
2354
2355
        return $query;
2356
    }
2357
2358
2359
    /**
2360
     * Get the link to the courses page. This will be the course post type archive
2361
     * page link or the page the user set in their settings
2362
     *
2363
     * @since 1.9.0
2364
     * @return string $course_page_url
2365
     */
2366
    public static function get_courses_page_url(){
2367
2368
        $course_page_id = intval( Sensei()->settings->settings[ 'course_page' ] );
2369
        $course_page_url = empty( $course_page_id ) ? get_post_type_archive_link('course') : get_permalink( $course_page_id );
2370
2371
        return $course_page_url;
2372
2373
    }// get_course_url
2374
2375
    /**
2376
     * Output the headers on the course archive page
2377
     *
2378
     * Hooked into the sensei_archive_title
2379
     *
2380
     * @since 1.9.0
2381
     * @param string $query_type
2382
     * @param string $before_html
2383
     * @param string $after_html
2384
     * @return void
2385
     */
2386
    public static function archive_header( $query_type ='' , $before_html='', $after_html =''  ){
2387
2388
        if( ! is_post_type_archive('course') ){
2389
            return;
2390
        }
2391
2392
        // deprecated since 1.9.0
2393
        sensei_do_deprecated_action('sensei_archive_title','1.9.0','sensei_archive_before_course_loop');
2394
2395
        $html = '';
2396
2397
        if( empty( $before_html ) ){
2398
2399
            $before_html = '<header class="archive-header"><h1>';
2400
2401
        }
2402
2403
        if( empty( $after_html ) ){
2404
2405
            $after_html = '</h1></header>';
2406
2407
        }
2408
2409
        if ( is_tax( 'course-category' ) ) {
2410
2411
            global $wp_query;
2412
2413
            $taxonomy_obj = $wp_query->get_queried_object();
2414
            $taxonomy_short_name = $taxonomy_obj->taxonomy;
2415
            $taxonomy_raw_obj = get_taxonomy( $taxonomy_short_name );
2416
            $title = sprintf( __( '%1$s Archives: %2$s', 'woothemes-sensei' ), $taxonomy_raw_obj->labels->name, $taxonomy_obj->name );
2417
            echo apply_filters( 'course_category_archive_title', $before_html . $title . $after_html );
2418
            return;
2419
2420
        } // End If Statement
2421
2422
        switch ( $query_type ) {
2423
            case 'newcourses':
2424
                $html .= $before_html . __( 'New Courses', 'woothemes-sensei' ) . $after_html;
2425
                break;
2426
            case 'featuredcourses':
2427
                $html .= $before_html .  __( 'Featured Courses', 'woothemes-sensei' ) . $after_html;
2428
                break;
2429
            case 'freecourses':
2430
                $html .= $before_html .  __( 'Free Courses', 'woothemes-sensei' ) . $after_html;
2431
                break;
2432
            case 'paidcourses':
2433
                $html .= $before_html .  __( 'Paid Courses', 'woothemes-sensei' ) . $after_html;
2434
                break;
2435
            default:
2436
                $html .= $before_html . __( 'Courses', 'woothemes-sensei' ) . $after_html;
2437
                break;
2438
        } // End Switch Statement
2439
2440
        echo apply_filters( 'course_archive_title', $html );
2441
2442
    }//course_archive_header
2443
2444
2445
    /**
2446
     * Filter the single course content
2447
     * taking into account if the user has access.
2448
     *
2449
     * @1.9.0
2450
     *
2451
     * @param string $content
2452
     * @return string $content or $excerpt
2453
     */
2454
    public static function single_course_content( $content ){
2455
2456
        if( ! is_singular('course') ){
2457
2458
            return $content;
2459
2460
        }
2461
2462
        // Content Access Permissions
2463
        $access_permission = false;
2464
2465
        if ( ! Sensei()->settings->get('access_permission')  || sensei_all_access() ) {
2466
2467
            $access_permission = true;
2468
2469
        } // End If Statement
2470
2471
        // Check if the user is taking the course
2472
        $is_user_taking_course = Sensei_Utils::user_started_course( get_the_ID(), get_current_user_id() );
2473
2474
        if(Sensei_WC::is_woocommerce_active()) {
2475
2476
            $wc_post_id = get_post_meta( get_the_ID(), '_course_woocommerce_product', true );
2477
            $product = Sensei()->sensei_get_woocommerce_product_object( $wc_post_id );
2478
2479
            $has_product_attached = isset ( $product ) && is_object ( $product );
2480
2481
        } else {
2482
2483
            $has_product_attached = false;
2484
2485
        }
2486
2487
        if ( ( is_user_logged_in() && $is_user_taking_course )
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_user_taking_course of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2488
            || ( $access_permission && !$has_product_attached)
2489
            || 'full' == Sensei()->settings->get( 'course_single_content_display' ) ) {
2490
2491
            return $content;
2492
2493
        } else {
2494
2495
            return '<p class="course-excerpt">' . get_post(  get_the_ID() )->post_excerpt . '</p>';
2496
2497
        }
2498
2499
    }// end single_course_content
2500
2501
    /**
2502
     * Output the the single course lessons title with markup.
2503
     *
2504
     * @since 1.9.0
2505
     */
2506
    public static function the_course_lessons_title(){
2507
        global $post;
2508
        $none_module_lessons = Sensei()->modules->get_none_module_lessons( $post->ID  );
2509
        $course_lessons = Sensei()->course->course_lessons( $post->ID );
2510
2511
        // title should be Other Lessons if there are lessons belonging to models.
2512
        $title = __('Other Lessons', 'woothemes-sensei');
2513
        if( count( $course_lessons ) == count( $none_module_lessons )  ){
2514
2515
            $title = __('Lessons', 'woothemes-sensei');
2516
2517
        }
2518
2519
        /**
2520
         * hook document in class-woothemes-sensei-message.php
2521
         */
2522
        $title = apply_filters( 'sensei_single_title', $title, $post->post_type );
2523
2524
        ob_start(); // start capturing the following output.
2525
2526
        ?>
2527
2528
            <header>
2529
                <h2> <?php echo $title; ?> </h2>
2530
            </header>
2531
2532
        <?php
2533
2534
        /**
2535
         * Filter the title and markup that appears above the lessons on a single course
2536
         * page.
2537
         *
2538
         * @since 1.9.0
2539
         * @param string $lessons_title_html
2540
         */
2541
        echo apply_filters('the_course_lessons_title', ob_get_clean() ); // output and filter the captured output and stop capturing.
2542
2543
    }// end the_course_lessons_title
2544
2545
    /**
2546
     * This function loads the global wp_query object with with lessons
2547
     * of the current course. It is designed to be used on the single-course template
2548
     * and expects the global post to be a singular course.
2549
     *
2550
     * This function excludes lessons belonging to modules as they are
2551
     * queried separately.
2552
     *
2553
     * @since 1.9.0
2554
     * @global $wp_query
2555
     */
2556
    public static function load_single_course_lessons_query(){
2557
2558
        global $post, $wp_query;
2559
2560
        $course_id = $post->ID;
2561
2562
        if( 'course' != get_post_type( $course_id ) ){
2563
            return;
2564
        }
2565
2566
        $course_lesson_query_args = array(
2567
            'post_type'         => 'lesson',
2568
            'posts_per_page'    => 500,
2569
            'orderby'           => 'date',
2570
            'order'             => 'ASC',
2571
            'meta_query'        => array(
2572
                array(
2573
                    'key' => '_lesson_course',
2574
                    'value' => intval( $course_id ),
2575
                ),
2576
            ),
2577
            'post_status'       => 'public',
2578
            'suppress_filters'  => 0,
2579
        );
2580
2581
        // Exclude lessons belonging to modules as they are queried along with the modules.
2582
        $modules = Sensei()->modules->get_course_modules( $course_id );
2583
        if( !is_wp_error( $modules ) && ! empty( $modules ) && is_array( $modules ) ){
2584
2585
            $terms_ids = array();
2586
            foreach( $modules as $term ){
2587
2588
                $terms_ids[] = $term->term_id;
2589
2590
            }
2591
2592
            $course_lesson_query_args[ 'tax_query'] = array(
2593
                array(
2594
                    'taxonomy' => 'module',
2595
                    'field'    => 'id',
2596
                    'terms'    => $terms_ids,
2597
                    'operator' => 'NOT IN',
2598
                ),
2599
            );
2600
        }
2601
2602
        $wp_query = new WP_Query( $course_lesson_query_args );
2603
2604
    }// load_single_course_lessons
2605
2606
    /**
2607
     * Flush the rewrite rules for a course post type
2608
     *
2609
     * @since 1.9.0
2610
     *
2611
     * @param $post_id
2612
     */
2613 View Code Duplication
    public static function flush_rewrite_rules( $post_id ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2614
2615
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
2616
2617
            return;
2618
2619
        }
2620
2621
2622
        if( 'course' == get_post_type( $post_id )  ){
2623
2624
            Sensei()->initiate_rewrite_rules_flush();
2625
2626
        }
2627
2628
    }
2629
2630
    /**
2631
     * Optionally return the full content on the single course pages
2632
     * depending on the users course_single_content_display setting
2633
     *
2634
     * @since 1.9.0
2635
     * @param $excerpt
2636
     * @return string
2637
     */
2638
    public static function full_content_excerpt_override( $excerpt ){
2639
2640
        if (   is_singular('course')  &&
2641
                'full' == Sensei()->settings->get( 'course_single_content_display' ) ){
2642
2643
            return get_the_content();
2644
2645
        } else {
2646
2647
            return $excerpt;
2648
2649
        }
2650
2651
    }
2652
2653
    /**
2654
     * Output the course actions like start taking course, register, add to cart etc.
2655
     *
2656
     * @since 1.9.0
2657
     */
2658
    public static function the_course_enrolment_actions(){
2659
        ?>
2660
        <section class="course-meta course-enrolment">
2661
        <?php
2662
        global  $post, $current_user;
2663
        $is_user_taking_course = Sensei_Utils::user_started_course( $post->ID, $current_user->ID );
2664
        if ( is_user_logged_in() && ! $is_user_taking_course ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_user_taking_course of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2665
2666
            // Get the product ID
2667
            $wc_post_id = absint( get_post_meta( $post->ID, '_course_woocommerce_product', true ) );
2668
2669
            // Check for woocommerce
2670
            if ( Sensei_WC::is_woocommerce_active() && ( 0 < intval( $wc_post_id ) ) ) {
2671
                sensei_wc_add_to_cart($post->ID);
0 ignored issues
show
Deprecated Code introduced by
The function sensei_wc_add_to_cart() has been deprecated with message: since Sensei_WC::the_add_to_cart_button_html( $course_id );

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2672
            } else {
2673
                sensei_start_course_form($post->ID);
2674
            } // End If Statement
2675
2676
        } elseif ( is_user_logged_in() ) {
2677
2678
            // Check if course is completed
2679
            $user_course_status = Sensei_Utils::user_course_status( $post->ID, $current_user->ID );
2680
            $completed_course = Sensei_Utils::user_completed_course( $user_course_status );
2681
            // Success message
2682
            if ( $completed_course ) { ?>
2683
                <div class="status completed"><?php  _e( 'Completed', 'woothemes-sensei' ); ?></div>
2684
                <?php
2685
                $has_quizzes = Sensei()->course->course_quizzes( $post->ID, true );
2686
                if( has_filter( 'sensei_results_links' ) || $has_quizzes ) { ?>
2687
                    <p class="sensei-results-links">
2688
                        <?php
2689
                        $results_link = '';
2690 View Code Duplication
                        if( $has_quizzes ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2691
                            $results_link = '<a class="view-results" href="' . Sensei()->course_results->get_permalink( $post->ID ) . '">' .  __( 'View results', 'woothemes-sensei' ) . '</a>';
2692
                        }
2693
                        $results_link = apply_filters( 'sensei_results_links', $results_link );
2694
                        echo $results_link;
2695
                        ?></p>
2696
                <?php } ?>
2697
            <?php } else { ?>
2698
                <div class="status in-progress"><?php echo __( 'In Progress', 'woothemes-sensei' ); ?></div>
2699
            <?php }
2700
2701
        } else {
2702
            // Get the product ID
2703
            $wc_post_id = absint( get_post_meta( $post->ID, '_course_woocommerce_product', true ) );
2704
            // Check for woocommerce
2705
            if ( Sensei_WC::is_woocommerce_active() && ( 0 < intval( $wc_post_id ) ) ) {
2706
2707
                sensei_wc_add_to_cart($post->ID);
0 ignored issues
show
Deprecated Code introduced by
The function sensei_wc_add_to_cart() has been deprecated with message: since Sensei_WC::the_add_to_cart_button_html( $course_id );

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2708
2709
            } else {
2710
2711
                if( get_option( 'users_can_register') ) {
2712
2713
2714
                    $my_courses_page_id = '';
2715
2716
                    /**
2717
                     * Filter to force Sensei to output the default WordPress user
2718
                     * registration link.
2719
                     *
2720
                     * @since 1.9.0
2721
                     * @param bool $wp_register_link default false
2722
                     */
2723
2724
                    $wp_register_link = apply_filters('sensei_use_wp_register_link', false);
2725
2726
                    $settings = Sensei()->settings->get_settings();
2727 View Code Duplication
                    if( isset( $settings[ 'my_course_page' ] )
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2728
                        && 0 < intval( $settings[ 'my_course_page' ] ) ){
2729
2730
                        $my_courses_page_id = $settings[ 'my_course_page' ];
2731
2732
                    }
2733
2734
                    // If a My Courses page was set in Settings, and 'sensei_use_wp_register_link'
2735
                    // is false, link to My Courses. If not, link to default WordPress registration page.
2736
                    if( !empty( $my_courses_page_id ) && $my_courses_page_id && !$wp_register_link){
2737
2738
                        $my_courses_url = get_permalink( $my_courses_page_id  );
2739
                        $register_link = '<a href="'.$my_courses_url. '">' . __('Register', 'woothemes-sensei') .'</a>';
2740
                        echo '<div class="status register">' . $register_link . '</div>' ;
2741
2742
                    } else{
2743
2744
                        wp_register( '<div class="status register">', '</div>' );
2745
2746
                    }
2747
2748
                } // end if user can register
2749
2750
            } // End If Statement
2751
2752
        } // End If Statement ?>
2753
2754
        </section><?php
2755
2756
    }// end the_course_enrolment_actions
2757
2758
    /**
2759
     * Output the course video inside the loop.
2760
     *
2761
     * @since 1.9.0
2762
     */
2763
    public static function the_course_video(){
2764
2765
        global $post;
2766
        // Get the meta info
2767
        $course_video_embed = get_post_meta( $post->ID, '_course_video_embed', true );
2768
2769
        if ( 'http' == substr( $course_video_embed, 0, 4) ) {
2770
2771
            $course_video_embed = wp_oembed_get( esc_url( $course_video_embed ) );
2772
2773
        } // End If Statement
2774
2775
        if ( '' != $course_video_embed ) { ?>
2776
2777
            <div class="course-video">
2778
                <?php echo html_entity_decode($course_video_embed); ?>
2779
            </div>
2780
2781
        <?php } // End If Statement
2782
    }
2783
2784
    /**
2785
     * Output the title for the single lesson page
2786
     *
2787
     * @global $post
2788
     * @since 1.9.0
2789
     */
2790
    public static function the_title(){
2791
2792
        global $post;
2793
2794
        ?>
2795
        <header>
2796
2797
            <h1>
2798
2799
                <?php
2800
                /**
2801
                 * Filter documented in class-sensei-messages.php the_title
2802
                 */
2803
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
2804
                ?>
2805
2806
            </h1>
2807
2808
        </header>
2809
2810
        <?php
2811
2812
    }//the_title
2813
2814
    /**
2815
     * Show the title on the course category pages
2816
     *
2817
     * @since 1.9.0
2818
     */
2819
    public static function course_category_title(){
2820
2821
        if( ! is_tax( 'course-category' ) ){
2822
            return;
2823
        }
2824
2825
        $category_slug = get_query_var('course-category');
2826
        $term  = get_term_by('slug',$category_slug,'course-category');
2827
2828
        if( ! empty($term) ){
2829
2830
            $title = $term->name;
2831
2832
        }else{
2833
2834
            $title = 'Course Category';
2835
2836
        }
2837
2838
        $html = '<h2 class="sensei-category-title">';
2839
        $html .= __('Category') . ' ' . $title;
2840
        $html .= '</h2>';
2841
2842
        echo apply_filters( 'course_category_title', $html , $term->term_id );
2843
2844
    }// course_category_title
2845
2846
    /**
2847
     * Alter the course query to respect the order set for courses and apply
2848
     * this on the course-category pages.
2849
     *
2850
     * @since 1.9.0
2851
     *
2852
     * @param WP_Query $query
2853
     * @return WP_Query
2854
     */
2855
    public static function alter_course_category_order( $query ){
2856
2857
        if( ! is_tax( 'course-category' ) || ! $query->is_main_query() ){
2858
            return $query;
2859
        }
2860
2861
        $order = get_option( 'sensei_course_order', '' );
2862
        if( !empty( $order )  ){
2863
            $query->set('orderby', 'menu_order' );
2864
            $query->set('order', 'ASC' );
2865
        }
2866
2867
        return $query;
2868
2869
    }
2870
2871
    /**
2872
     * The very basic course query arguments
2873
     * so we don't have to repeat this througout
2874
     * the code base.
2875
     *
2876
     * Usage:
2877
     * $args = Sensei_Course::get_default_query_args();
2878
     * $args['custom_arg'] ='custom value';
2879
     * $courses = get_posts( $args )
2880
     *
2881
     * @since 1.9.0
2882
     *
2883
     * @return array
2884
     */
2885
    public static function get_default_query_args(){
2886
        return array(
2887
            'post_type' 		=> 'course',
2888
            'posts_per_page' 		=> 1000,
2889
            'orderby'         	=> 'date',
2890
            'order'           	=> 'DESC',
2891
            'suppress_filters' 	=> 0
2892
        );
2893
    }
2894
2895
    /**
2896
     * Check if the prerequisite course is completed
2897
     * Courses with no pre-requisite should always return true
2898
     *
2899
     * @since 1.9.0
2900
     * @param $course_id
2901
     * @return bool
2902
     */
2903
    public static function is_prerequisite_complete( $course_id ){
2904
2905
        $course_prerequisite_id = get_post_meta( $course_id, '_course_prerequisite', true );
2906
2907
        // if it has a pre requisite course check it
2908
        if( ! empty(  $course_prerequisite_id ) ){
2909
2910
            return Sensei_Utils::user_completed_course( $course_prerequisite_id, get_current_user_id() );
2911
2912
        }
2913
2914
        return true;
2915
2916
    }// end is_prerequisite_complete
2917
2918
2919
}// End Class
2920
2921
/**
2922
 * Class WooThemes_Sensei_Course
2923
 * @ignore only for backward compatibility
2924
 * @since 1.9.0
2925
 */
2926
class WooThemes_Sensei_Course extends Sensei_Course{}
2927