Completed
Push — master ( d89e0e...cacfd4 )
by Dwain
05:51
created

Sensei_Course::the_course_video()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 12

Duplication

Lines 24
Ratio 100 %
Metric Value
dl 24
loc 24
rs 8.6845
cc 4
eloc 12
nc 5
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
765
	        $course_id = $course_id->ID;
766
767
        }
768
769
		if ( 'course' !== get_post_type( $course_id )  ){
770
771
			return;
772
773
		}
774
775
		$html = '';
776
777
		// Get Width and Height settings
778
		if ( ( $width == '100' ) && ( $height == '100' ) ) {
779
780
			if ( is_singular( 'course' ) ) {
781
782
				if ( !Sensei()->settings->settings[ 'course_single_image_enable' ] ) {
783
					return '';
784
				} // End If Statement
785
				$image_thumb_size = 'course_single_image';
786
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
787
				$width = $dimensions['width'];
788
				$height = $dimensions['height'];
789
790
			} else {
791
792
				if ( !Sensei()->settings->settings[ 'course_archive_image_enable' ] ) {
793
					return '';
794
				} // End If Statement
795
796
				$image_thumb_size = 'course_archive_image';
797
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
798
				$width = $dimensions['width'];
799
				$height = $dimensions['height'];
800
801
			} // End If Statement
802
803
		} // End If Statement
804
805
		$img_url = '';
806
		if ( has_post_thumbnail( $course_id ) ) {
807
   			// Get Featured Image
808
   			$img_url = get_the_post_thumbnail( $course_id, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
809
 		} else {
810
811
			// Check for a Lesson Image
812
			$course_lessons = $this->course_lessons( $course_id );
813
814
			foreach ($course_lessons as $lesson_item){
815
				if ( has_post_thumbnail( $lesson_item->ID ) ) {
816
					// Get Featured Image
817
					$img_url = get_the_post_thumbnail( $lesson_item->ID, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
818
					if ( '' != $img_url ) {
819
						break;
820
					} // End If Statement
821
822
				} // End If Statement
823
			} // End For Loop
824
825 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...
826
827
 				// Display Image Placeholder if none
828
				if ( Sensei()->settings->get( 'placeholder_images_enable' ) ) {
829
830
                    $img_url = apply_filters( 'sensei_course_placeholder_image_url', '<img src="http://placehold.it/' . $width . 'x' . $height . '" class="woo-image thumbnail alignleft" />' );
831
832
				} // End If Statement
833
834
 			} // End If Statement
835
836
		} // End If Statement
837
838
		if ( '' != $img_url ) {
839
840
			$html .= '<a href="' . get_permalink( $course_id ) . '" title="' . esc_attr( get_post_field( 'post_title', $course_id ) ) . '">' . $img_url  .'</a>';
841
842
		} // End If Statement
843
844
        if( $return ){
845
846
            return $html;
847
848
        }else{
849
850
            echo $html;
851
852
        }
853
854
	} // End course_image()
855
856
857
	/**
858
	 * course_count function.
859
	 *
860
	 * @access public
861
	 * @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...
862
	 * @param string $post_status (default: 'publish')
863
	 * @return int
864
	 */
865
	public function course_count( $post_status = 'publish' ) {
866
867
		$post_args = array(	'post_type'         => 'course',
868
							'posts_per_page'    => -1,
869
//							'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...
870
//							'order'             => 'ASC',
871
							'post_status'       => $post_status,
872
							'suppress_filters'  => 0,
873
							'fields'            => 'ids',
874
							);
875
876
		// Allow WP to generate the complex final query, just shortcut to only do an overall count
877
//		add_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
878
		$courses_query = new WP_Query( apply_filters( 'sensei_course_count', $post_args ) );
879
//		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...
880
881
		return count( $courses_query->posts );
882
	} // End course_count()
883
884
885
	/**
886
	 * course_lessons function.
887
	 *
888
	 * @access public
889
	 * @param int $course_id (default: 0)
890
	 * @param string $post_status (default: 'publish')
891
	 * @param string $fields (default: 'all'). WP only allows 3 types, but we will limit it to only 'ids' or 'all'
892
	 * @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...
893
	 */
894
	public function course_lessons( $course_id = 0, $post_status = 'publish', $fields = 'all' ) {
895
896
        if( is_a( $course_id, 'WP_Post' ) ){
897
            $course_id = $course_id->ID;
898
        }
899
900
		$post_args = array(	'post_type'         => 'lesson',
901
							'posts_per_page'       => -1,
902
							'orderby'           => 'date',
903
							'order'             => 'ASC',
904
							'meta_query'        => array(
905
								array(
906
									'key' => '_lesson_course',
907
									'value' => intval( $course_id ),
908
								),
909
							),
910
							'post_status'       => $post_status,
911
							'suppress_filters'  => 0,
912
							);
913
		$query_results = new WP_Query( $post_args );
914
        $lessons = $query_results->posts;
915
916
        // re order the lessons. This could not be done via the OR meta query as there may be lessons
917
        // with the course order for a different course and this should not be included. It could also not
918
        // be done via the AND meta query as it excludes lesson that does not have the _order_$course_id but
919
        // that have been added to the course.
920
        if( count( $lessons) > 1  ){
921
922
            foreach( $lessons as $lesson ){
923
924
                $order = intval( get_post_meta( $lesson->ID, '_order_'. $course_id, true ) );
925
                // for lessons with no order set it to be 10000 so that it show up at the end
926
                $lesson->course_order = $order ? $order : 100000;
927
            }
928
929
            uasort( $lessons, array( $this, '_short_course_lessons_callback' )   );
930
        }
931
932
        /**
933
         * Filter runs inside Sensei_Course::course_lessons function
934
         *
935
         * Returns all lessons for a given course
936
         *
937
         * @param array $lessons
938
         * @param int $course_id
939
         */
940
        $lessons = apply_filters( 'sensei_course_get_lessons', $lessons, $course_id  );
941
942
        //return the requested fields
943
        // runs after the sensei_course_get_lessons filter so the filter always give an array of lesson
944
        // objects
945
        if( 'ids' == $fields ) {
946
            $lesson_objects = $lessons;
947
            $lessons = array();
948
949
            foreach ($lesson_objects as $lesson) {
950
                $lessons[] = $lesson->ID;
951
            }
952
        }
953
954
        return $lessons;
955
956
	} // End course_lessons()
957
958
    /**
959
     * Used for the uasort in $this->course_lessons()
960
     * @since 1.8.0
961
     * @access protected
962
     *
963
     * @param array $lesson_1
964
     * @param array $lesson_2
965
     * @return int
966
     */
967
    protected function _short_course_lessons_callback( $lesson_1, $lesson_2 ){
968
969
        if ( $lesson_1->course_order == $lesson_2->course_order ) {
970
            return 0;
971
        }
972
973
        return ($lesson_1->course_order < $lesson_2->course_order) ? -1 : 1;
974
    }
975
976
	/**
977
	 * Fetch all quiz ids in a course
978
	 * @since  1.5.0
979
	 * @param  integer $course_id ID of course
980
	 * @param  boolean $boolean_check True if a simple yes/no is required
981
	 * @return array              Array of quiz post objects
982
	 */
983
	public function course_quizzes( $course_id = 0, $boolean_check = false ) {
984
985
986
		$course_quizzes = array();
987
988
		if( $course_id ) {
989
			$lesson_ids = Sensei()->course->course_lessons( $course_id, 'any', 'ids' );
990
991
			foreach( $lesson_ids as $lesson_id ) {
992
				$has_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
993
				if ( $has_questions && $boolean_check ) {
994
					return true;
995
				}
996
				elseif ( $has_questions ) {
997
					$quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
998
//					$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...
999
//					if( count( $questions ) > 0 ) {
1000
						$course_quizzes[] = $quiz_id;
1001
//					}
1002
				}
1003
			}
1004
		}
1005
		if ( $boolean_check && empty($course_quizzes) ) {
1006
			$course_quizzes = false;
1007
		}
1008
		return $course_quizzes;
1009
	}
1010
1011
1012
	/**
1013
	 * course_lessons_completed function. Appears to be completely unused and a duplicate of course_lessons()!
1014
	 *
1015
	 * @access public
1016
	 * @param  int $course_id (default: 0)
1017
	 * @param  string $post_status (default: 'publish')
1018
	 * @return array
1019
	 */
1020
	public function course_lessons_completed( $course_id = 0, $post_status = 'publish' ) {
1021
1022
		return $this->course_lessons( $course_id, $post_status );
1023
1024
	} // End course_lessons_completed()
1025
1026
1027
	/**
1028
	 * course_author_lesson_count function.
1029
	 *
1030
	 * @access public
1031
	 * @param  int $author_id (default: 0)
1032
	 * @param  int $course_id (default: 0)
1033
	 * @return int
1034
	 */
1035 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...
1036
1037
        $lesson_args = array(	'post_type' 		=> 'lesson',
1038
								'posts_per_page' 		=> -1,
1039
		    					'author'         	=> $author_id,
1040
		    					'meta_key'        	=> '_lesson_course',
1041
    							'meta_value'      	=> $course_id,
1042
    	    					'post_status'      	=> 'publish',
1043
    	    					'suppress_filters' 	=> 0,
1044
								'fields'            => 'ids', // less data to retrieve
1045
		    				);
1046
		$lessons_array = get_posts( $lesson_args );
1047
		$count = count( $lessons_array );
1048
		return $count;
1049
1050
	} // End course_author_lesson_count()
1051
1052
	/**
1053
	 * course_lesson_count function.
1054
	 *
1055
	 * @access public
1056
	 * @param  int $course_id (default: 0)
1057
	 * @return int
1058
	 */
1059 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...
1060
1061
		$lesson_args = array(	'post_type' 		=> 'lesson',
1062
								'posts_per_page' 		=> -1,
1063
		    					'meta_key'        	=> '_lesson_course',
1064
    							'meta_value'      	=> $course_id,
1065
    	    					'post_status'      	=> 'publish',
1066
    	    					'suppress_filters' 	=> 0,
1067
								'fields'            => 'ids', // less data to retrieve
1068
		    				);
1069
		$lessons_array = get_posts( $lesson_args );
1070
1071
        $count = count( $lessons_array );
1072
1073
        return $count;
1074
1075
	} // End course_lesson_count()
1076
1077
	/**
1078
	 * course_lesson_preview_count function.
1079
	 *
1080
	 * @access public
1081
	 * @param  int $course_id (default: 0)
1082
	 * @return int
1083
	 */
1084 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...
1085
1086
		$lesson_args = array(	'post_type' 		=> 'lesson',
1087
								'posts_per_page' 		=> -1,
1088
    	    					'post_status'      	=> 'publish',
1089
    	    					'suppress_filters' 	=> 0,
1090
    	    					'meta_query' => array(
1091
									array(
1092
										'key' => '_lesson_course',
1093
										'value' => $course_id
1094
									),
1095
									array(
1096
										'key' => '_lesson_preview',
1097
										'value' => 'preview'
1098
									)
1099
								),
1100
								'fields'            => 'ids', // less data to retrieve
1101
		    				);
1102
		$lessons_array = get_posts( $lesson_args );
1103
1104
		$count = count( $lessons_array );
1105
1106
        return $count;
1107
1108
	} // End course_lesson_count()
1109
1110
	/**
1111
	 * get_product_courses function.
1112
	 *
1113
	 * @access public
1114
	 * @param  int $product_id (default: 0)
1115
	 * @return array
1116
	 */
1117 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...
1118
1119
		$posts_array = array();
1120
		// Check for WooCommerce
1121
		if ( Sensei_WC::is_woocommerce_active() && 0 < $product_id ) {
1122
			$post_args = array(	'post_type' 		=> 'course',
1123
								'posts_per_page' 		=> -1,
1124
								'meta_key'        	=> '_course_woocommerce_product',
1125
	    						'meta_value'      	=> $product_id,
1126
	    						'post_status'       => 'publish',
1127
								'suppress_filters' 	=> 0,
1128
								'orderby' 			=> 'menu_order date',
1129
								'order' 			=> 'ASC',
1130
								);
1131
			$posts_array = get_posts( $post_args );
1132
		} // End If Statement
1133
		return $posts_array;
1134
1135
	} // End get_product_courses()
1136
1137
	/**
1138
	 * Fix posts_per_page for My Courses page
1139
	 * @param  WP_Query $query
1140
	 * @return void
1141
	 */
1142
	public function filter_my_courses( $query ) {
1143
		global  $my_courses_section;
1144
1145 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...
1146
			$amount = absint( Sensei()->settings->settings[ 'my_course_amount' ] );
1147
			$query->set( 'posts_per_page', $amount );
1148
		}
1149
1150
		if( isset( $_GET[ $my_courses_section . '_page' ] ) && 0 < intval( $_GET[ $my_courses_section . '_page' ] ) ) {
1151
			$page = intval( $_GET[ $my_courses_section . '_page' ] );
1152
			$query->set( 'paged', $page );
1153
		}
1154
	}
1155
1156
	/**
1157
	 * load_user_courses_content generates HTML for user's active & completed courses
1158
     *
1159
     * This function also ouputs the html so no need to echo the content.
1160
     *
1161
	 * @since  1.4.0
1162
	 * @param  object  $user   Queried user object
1163
	 * @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...
1164
	 * @return string          HTML displayng course data
1165
	 */
1166
	public function load_user_courses_content( $user = false ) {
1167
		global $course, $my_courses_page, $my_courses_section;
1168
1169
        if( ! isset( Sensei()->settings->settings[ 'learner_profile_show_courses' ] )
1170
            || ! Sensei()->settings->settings[ 'learner_profile_show_courses' ] ) {
1171
1172
            // do not show the content if the settings doesn't allow for it
1173
            return;
1174
1175
        }
1176
1177
        $manage = ( $user->ID == get_current_user_id() ) ? true : false;
1178
1179
        do_action( 'sensei_before_learner_course_content', $user );
1180
1181
		// Build Output HTML
1182
		$complete_html = $active_html = '';
1183
1184
		if( is_a( $user, 'WP_User' ) ) {
1185
1186
			$my_courses_page = true;
1187
1188
			// Allow action to be run before My Courses content has loaded
1189
			do_action( 'sensei_before_my_courses', $user->ID );
1190
1191
			// Logic for Active and Completed Courses
1192
			$per_page = 20;
1193
			if ( isset( Sensei()->settings->settings[ 'my_course_amount' ] )
1194
                && ( 0 < absint( Sensei()->settings->settings[ 'my_course_amount' ] ) ) ) {
1195
1196
				$per_page = absint( Sensei()->settings->settings[ 'my_course_amount' ] );
1197
1198
			}
1199
1200
			$course_statuses = Sensei_Utils::sensei_check_for_activity( array( 'user_id' => $user->ID, 'type' => 'sensei_course_status' ), true );
1201
			// User may only be on 1 Course
1202
			if ( !is_array($course_statuses) ) {
1203
				$course_statuses = array( $course_statuses );
1204
			}
1205
			$completed_ids = $active_ids = array();
1206 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...
1207
				if ( Sensei_Utils::user_completed_course( $course_status, $user->ID ) ) {
1208
					$completed_ids[] = $course_status->comment_post_ID;
1209
				} else {
1210
					$active_ids[] = $course_status->comment_post_ID;
1211
				}
1212
			}
1213
1214
			$active_count = $completed_count = 0;
1215
1216
			$active_courses = array();
1217 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...
1218
				$my_courses_section = 'active';
1219
				$active_courses = Sensei()->course->course_query( $per_page, 'usercourses', $active_ids );
1220
				$active_count = count( $active_ids );
1221
			} // End If Statement
1222
1223
			$completed_courses = array();
1224 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...
1225
				$my_courses_section = 'completed';
1226
				$completed_courses = Sensei()->course->course_query( $per_page, 'usercourses', $completed_ids );
1227
				$completed_count = count( $completed_ids );
1228
			} // End If Statement
1229
1230
			foreach ( $active_courses as $course_item ) {
1231
1232
				$course_lessons =  Sensei()->course->course_lessons( $course_item->ID );
1233
				$lessons_completed = 0;
1234
				foreach ( $course_lessons as $lesson ) {
1235
					if ( Sensei_Utils::user_completed_lesson( $lesson->ID, $user->ID ) ) {
1236
						++$lessons_completed;
1237
					}
1238
				}
1239
1240
			    // Get Course Categories
1241
			    $category_output = get_the_term_list( $course_item->ID, 'course-category', '', ', ', '' );
1242
1243
                $active_html .= '<article class="' . esc_attr( join( ' ', get_post_class( array( 'course', 'post' ), $course_item->ID ) ) ) . '">';
1244
1245
                // Image
1246
                $active_html .= Sensei()->course->course_image( absint( $course_item->ID ), '100','100', true );
1247
1248
                // Title
1249
                $active_html .= '<header>';
1250
1251
                $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>';
1252
1253
                $active_html .= '</header>';
1254
1255
                $active_html .= '<section class="entry">';
1256
1257
                $active_html .= '<p class="sensei-course-meta">';
1258
1259
                // Author
1260
                $user_info = get_userdata( absint( $course_item->post_author ) );
1261 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...
1262
                    && ( Sensei()->settings->settings[ 'course_author' ] ) ) {
1263
1264
                    $active_html .= '<span class="course-author">'
1265
                        . __( 'by ', 'woothemes-sensei' )
1266
                        . '<a href="' . esc_url( get_author_posts_url( absint( $course_item->post_author ) ) )
1267
                        . '" title="' . esc_attr( $user_info->display_name ) . '">'
1268
                        . esc_html( $user_info->display_name )
1269
                        . '</a></span>';
1270
1271
                } // End If Statement
1272
1273
                // Lesson count for this author
1274
                $lesson_count = Sensei()->course->course_lesson_count( absint( $course_item->ID ) );
1275
                // Handle Division by Zero
1276
                if ( 0 == $lesson_count ) {
1277
1278
                    $lesson_count = 1;
1279
1280
                } // End If Statement
1281
                $active_html .= '<span class="course-lesson-count">' . $lesson_count . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' ) . '</span>';
1282
                // Course Categories
1283
                if ( '' != $category_output ) {
1284
1285
                    $active_html .= '<span class="course-category">' . sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ) . '</span>';
1286
1287
                } // End If Statement
1288
                $active_html .= '<span class="course-lesson-progress">' . sprintf( __( '%1$d of %2$d lessons completed', 'woothemes-sensei' ) , $lessons_completed, $lesson_count  ) . '</span>';
1289
1290
                $active_html .= '</p>';
1291
1292
                $active_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>';
1293
1294
1295
1296
                $progress_percentage = abs( round( ( doubleval( $lessons_completed ) * 100 ) / ( $lesson_count ), 0 ) );
1297
1298
                $active_html .= $this->get_progress_meter( $progress_percentage );
1299
1300
                $active_html .= '</section>';
1301
1302
                if( is_user_logged_in() ) {
1303
1304
                    $active_html .= '<section class="entry-actions">';
1305
1306
                    $active_html .= '<form method="POST" action="' . esc_url( remove_query_arg( array( 'active_page', 'completed_page' ) ) ) . '">';
1307
1308
                    $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' ) ) . '" />';
1309
1310
                    $active_html .= '<input type="hidden" name="course_complete_id" id="course-complete-id" value="' . esc_attr( absint( $course_item->ID ) ) . '" />';
1311
1312
                    if ( 0 < absint( count( $course_lessons ) )
1313
                        && Sensei()->settings->settings['course_completion'] == 'complete' ){
1314
1315
                        $active_html .= '<span><input name="course_complete" type="submit" class="course-complete" value="'
1316
                            .  __( 'Mark as Complete', 'woothemes-sensei' ) . '"/> </span>';
1317
1318
                    } // End If Statement
1319
1320
                    $course_purchased = false;
1321
                    if ( Sensei_WC::is_woocommerce_active() ) {
1322
1323
                        // Get the product ID
1324
                        $wc_post_id = get_post_meta( absint( $course_item->ID ), '_course_woocommerce_product', true );
1325
                        if ( 0 < $wc_post_id ) {
1326
1327
                            $course_purchased = Sensei_WC::has_customer_bought_product(  $user->ID, $wc_post_id );
1328
1329
                        } // End If Statement
1330
1331
                    } // End If Statement
1332
1333
                    if ( false == $course_purchased ) {
1334
1335
                        $active_html .= '<span><input name="course_complete" type="submit" class="course-delete" value="'
1336
                            .  __( 'Delete Course', 'woothemes-sensei' ) . '"/></span>';
1337
1338
                    } // End If Statement
1339
1340
                    $active_html .= '</form>';
1341
1342
                    $active_html .= '</section>';
1343
                }
1344
1345
                $active_html .= '</article>';
1346
			}
1347
1348
			// Active pagination
1349 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...
1350
1351
				$current_page = 1;
1352
				if( isset( $_GET['active_page'] ) && 0 < intval( $_GET['active_page'] ) ) {
1353
					$current_page = $_GET['active_page'];
1354
				}
1355
1356
				$active_html .= '<nav class="pagination woo-pagination">';
1357
				$total_pages = ceil( $active_count / $per_page );
1358
1359
				if( $current_page > 1 ) {
1360
					$prev_link = add_query_arg( 'active_page', $current_page - 1 );
1361
					$active_html .= '<a class="prev page-numbers" href="' . esc_url( $prev_link ) . '">' . __( 'Previous' , 'woothemes-sensei' ) . '</a> ';
1362
				}
1363
1364
				for ( $i = 1; $i <= $total_pages; $i++ ) {
1365
					$link = add_query_arg( 'active_page', $i );
1366
1367
					if( $i == $current_page ) {
1368
						$active_html .= '<span class="page-numbers current">' . $i . '</span> ';
1369
					} else {
1370
						$active_html .= '<a class="page-numbers" href="' . esc_url( $link ). '">' . $i . '</a> ';
1371
					}
1372
				}
1373
1374
				if( $current_page < $total_pages ) {
1375
					$next_link = add_query_arg( 'active_page', $current_page + 1 );
1376
					$active_html .= '<a class="next page-numbers" href="' . esc_url( $next_link ) . '">' . __( 'Next' , 'woothemes-sensei' ) . '</a> ';
1377
				}
1378
1379
				$active_html .= '</nav>';
1380
			}
1381
1382
			foreach ( $completed_courses as $course_item ) {
1383
				$course = $course_item;
1384
1385
			    // Get Course Categories
1386
			    $category_output = get_the_term_list( $course_item->ID, 'course-category', '', ', ', '' );
1387
1388
		    	$complete_html .= '<article class="' . join( ' ', get_post_class( array( 'course', 'post' ), $course_item->ID ) ) . '">';
1389
1390
		    	    // Image
1391
		    		$complete_html .= Sensei()->course->course_image( absint( $course_item->ID ),100, 100, true );
1392
1393
		    		// Title
1394
		    		$complete_html .= '<header>';
1395
1396
		    		    $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>';
1397
1398
		    		$complete_html .= '</header>';
1399
1400
		    		$complete_html .= '<section class="entry">';
1401
1402
		    			$complete_html .= '<p class="sensei-course-meta">';
1403
1404
		    		    	// Author
1405
		    		    	$user_info = get_userdata( absint( $course_item->post_author ) );
1406 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...
1407
		    		    		$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>';
1408
		    		    	} // End If Statement
1409
1410
		    		    	// Lesson count for this author
1411
		    		    	$complete_html .= '<span class="course-lesson-count">'
1412
                                . Sensei()->course->course_lesson_count( absint( $course_item->ID ) )
1413
                                . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' )
1414
                                . '</span>';
1415
1416
		    		    	// Course Categories
1417
		    		    	if ( '' != $category_output ) {
1418
1419
		    		    		$complete_html .= '<span class="course-category">' . sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ) . '</span>';
1420
1421
		    		    	} // End If Statement
1422
1423
						$complete_html .= '</p>';
1424
1425
						$complete_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>';
1426
1427
                        $complete_html .= $this->get_progress_meter( 100 );
1428
1429
						if( $manage ) {
1430
							$has_quizzes = Sensei()->course->course_quizzes( $course_item->ID, true );
1431
							// Output only if there is content to display
1432
							if ( has_filter( 'sensei_results_links' ) || $has_quizzes ) {
1433
1434
1435
								$complete_html .= '<p class="sensei-results-links">';
1436
								$results_link = '';
1437 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...
1438
1439
									$results_link = '<a class="button view-results" href="'
1440
                                        . Sensei()->course_results->get_permalink( $course_item->ID )
1441
                                        . '">' . __( 'View results', 'woothemes-sensei' )
1442
                                        . '</a>';
1443
								}
1444
                                /**
1445
                                 * Filter documented in Sensei_Course::the_course_action_buttons
1446
                                 */
1447
								$complete_html .= apply_filters( 'sensei_results_links', $results_link, $course_item->ID );
1448
								$complete_html .= '</p>';
1449
1450
							}
1451
						}
1452
1453
		    		$complete_html .= '</section>';
1454
1455
		    	$complete_html .= '</article>';
1456
			}
1457
1458
			// Active pagination
1459 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...
1460
1461
				$current_page = 1;
1462
				if( isset( $_GET['completed_page'] ) && 0 < intval( $_GET['completed_page'] ) ) {
1463
					$current_page = $_GET['completed_page'];
1464
				}
1465
1466
				$complete_html .= '<nav class="pagination woo-pagination">';
1467
				$total_pages = ceil( $completed_count / $per_page );
1468
1469
1470
				if( $current_page > 1 ) {
1471
					$prev_link = add_query_arg( 'completed_page', $current_page - 1 );
1472
					$complete_html .= '<a class="prev page-numbers" href="' . esc_url( $prev_link ) . '">' . __( 'Previous' , 'woothemes-sensei' ) . '</a> ';
1473
				}
1474
1475
				for ( $i = 1; $i <= $total_pages; $i++ ) {
1476
					$link = add_query_arg( 'completed_page', $i );
1477
1478
					if( $i == $current_page ) {
1479
						$complete_html .= '<span class="page-numbers current">' . $i . '</span> ';
1480
					} else {
1481
						$complete_html .= '<a class="page-numbers" href="' . esc_url( $link ) . '">' . $i . '</a> ';
1482
					}
1483
				}
1484
1485
				if( $current_page < $total_pages ) {
1486
					$next_link = add_query_arg( 'completed_page', $current_page + 1 );
1487
					$complete_html .= '<a class="next page-numbers" href="' . esc_url( $next_link ) . '">' . __( 'Next' , 'woothemes-sensei' ) . '</a> ';
1488
				}
1489
1490
				$complete_html .= '</nav>';
1491
			}
1492
1493
		} // End If Statement
1494
1495
		if( $manage ) {
1496
			$no_active_message = __( 'You have no active courses.', 'woothemes-sensei' );
1497
			$no_complete_message = __( 'You have not completed any courses yet.', 'woothemes-sensei' );
1498
		} else {
1499
			$no_active_message =  __( 'This learner has no active courses.', 'woothemes-sensei' );
1500
			$no_complete_message =  __( 'This learner has not completed any courses yet.', 'woothemes-sensei' );
1501
		}
1502
1503
		ob_start();
1504
		?>
1505
1506
		<?php do_action( 'sensei_before_user_courses' ); ?>
1507
1508
		<?php
1509
		if( $manage && ( ! isset( Sensei()->settings->settings['messages_disable'] ) || ! Sensei()->settings->settings['messages_disable'] ) ) {
1510
			?>
1511
			<p class="my-messages-link-container">
1512
                <a class="my-messages-link" href="<?php echo get_post_type_archive_link( 'sensei_message' ); ?>"
1513
                   title="<?php _e( 'View & reply to private messages sent to your course & lesson teachers.', 'woothemes-sensei' ); ?>">
1514
                    <?php _e( 'My Messages', 'woothemes-sensei' ); ?>
1515
                </a>
1516
            </p>
1517
			<?php
1518
		}
1519
		?>
1520
		<div id="my-courses">
1521
1522
		    <ul>
1523
		    	<li><a href="#active-courses"><?php  _e( 'Active Courses', 'woothemes-sensei' ); ?></a></li>
1524
		    	<li><a href="#completed-courses"><?php  _e( 'Completed Courses', 'woothemes-sensei' ); ?></a></li>
1525
		    </ul>
1526
1527
		    <?php do_action( 'sensei_before_active_user_courses' ); ?>
1528
1529
		    <?php
1530
            $course_page_url = Sensei_Course::get_courses_page_url();
1531
            ?>
1532
1533
		    <div id="active-courses">
1534
1535
		    	<?php if ( '' != $active_html ) {
1536
1537
		    		echo $active_html;
1538
1539
		    	} else { ?>
1540
1541
		    		<div class="sensei-message info">
1542
1543
                        <?php echo $no_active_message; ?>
1544
1545
                        <a href="<?php echo $course_page_url; ?>">
1546
1547
                            <?php  _e( 'Start a Course!', 'woothemes-sensei' ); ?>
1548
1549
                        </a>
1550
1551
                    </div>
1552
1553
		    	<?php } // End If Statement ?>
1554
1555
		    </div>
1556
1557
		    <?php do_action( 'sensei_after_active_user_courses' ); ?>
1558
1559
		    <?php do_action( 'sensei_before_completed_user_courses' ); ?>
1560
1561
		    <div id="completed-courses">
1562
1563
		    	<?php if ( '' != $complete_html ) {
1564
1565
		    		echo $complete_html;
1566
1567
		    	} else { ?>
1568
1569
		    		<div class="sensei-message info">
1570
1571
                        <?php echo $no_complete_message; ?>
1572
1573
                    </div>
1574
1575
		    	<?php } // End If Statement ?>
1576
1577
		    </div>
1578
1579
		    <?php do_action( 'sensei_after_completed_user_courses' ); ?>
1580
1581
		</div>
1582
1583
		<?php do_action( 'sensei_after_user_courses' ); ?>
1584
1585
		<?php
1586
        echo ob_get_clean();
1587
1588
        do_action( 'sensei_after_learner_course_content', $user );
1589
1590
	} // end load_user_courses_content
1591
1592
    /**
1593
     * Returns a list of all courses
1594
     *
1595
     * @since 1.8.0
1596
     * @return array $courses{
1597
     *  @type $course WP_Post
1598
     * }
1599
     */
1600
    public static function get_all_courses(){
1601
1602
        $args = array(
1603
               'post_type' => 'course',
1604
                'posts_per_page' 		=> -1,
1605
                'orderby'         	=> 'title',
1606
                'order'           	=> 'ASC',
1607
                'post_status'      	=> 'any',
1608
                'suppress_filters' 	=> 0,
1609
        );
1610
1611
        $wp_query_obj =  new WP_Query( $args );
1612
1613
        /**
1614
         * sensei_get_all_courses filter
1615
         *
1616
         * This filter runs inside Sensei_Course::get_all_courses.
1617
         *
1618
         * @param array $courses{
1619
         *  @type WP_Post
1620
         * }
1621
         * @param array $attributes
1622
         */
1623
        return apply_filters( 'sensei_get_all_courses' , $wp_query_obj->posts );
1624
1625
    }// end get_all_courses
1626
1627
    /**
1628
     * Generate the course meter component
1629
     *
1630
     * @since 1.8.0
1631
     * @param int $progress_percentage 0 - 100
1632
     * @return string $progress_bar_html
1633
     */
1634
    public function get_progress_meter( $progress_percentage ){
1635
1636
        if ( 50 < $progress_percentage ) {
1637
            $class = ' green';
1638
        } elseif ( 25 <= $progress_percentage && 50 >= $progress_percentage ) {
1639
            $class = ' orange';
1640
        } else {
1641
            $class = ' red';
1642
        }
1643
        $progress_bar_html = '<div class="meter' . esc_attr( $class ) . '"><span style="width: ' . $progress_percentage . '%">' . round( $progress_percentage ) . '%</span></div>';
1644
1645
        return $progress_bar_html;
1646
1647
    }// end get_progress_meter
1648
1649
    /**
1650
     * Generate a statement that tells users
1651
     * how far they are in the course.
1652
     *
1653
     * @param int $course_id
1654
     * @param int $user_id
1655
     *
1656
     * @return string $statement_html
1657
     */
1658
    public function get_progress_statement( $course_id, $user_id ){
1659
1660
        if( empty( $course_id ) || empty( $user_id )
1661
        || ! 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...
1662
            return '';
1663
        }
1664
1665
        $completed = count( $this->get_completed_lesson_ids( $course_id, $user_id ) );
1666
        $total_lessons = count( $this->course_lessons( $course_id ) );
1667
1668
        $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 );
1669
1670
        /**
1671
         * Filter the course completion statement.
1672
         * Default Currently completed $var lesson($plural) of $var in total
1673
         *
1674
         * @param string $statement
1675
         */
1676
        return apply_filters( 'sensei_course_completion_statement', $statement );
1677
1678
    }// end generate_progress_statement
1679
1680
    /**
1681
     * Output the course progress statement
1682
     *
1683
     * @param $course_id
1684
     * @return void
1685
     */
1686
    public function the_progress_statement( $course_id = 0, $user_id = 0 ){
1687
        if( empty( $course_id ) ){
1688
            global $post;
1689
            $course_id = $post->ID;
1690
        }
1691
1692
        if( empty( $user_id ) ){
1693
            $user_id = get_current_user_id();
1694
        }
1695
1696
        echo '<span class="progress statement  course-completion-rate">' . $this->get_progress_statement( $course_id, $user_id  ) . '</span>';
1697
    }
1698
1699
    /**
1700
     * Output the course progress bar
1701
     *
1702
     * @param $course_id
1703
     * @return void
1704
     */
1705
    public function the_progress_meter( $course_id = 0, $user_id = 0 ){
1706
1707
        if( empty( $course_id ) ){
1708
            global $post;
1709
            $course_id = $post->ID;
1710
        }
1711
1712
        if( empty( $user_id ) ){
1713
            $user_id = get_current_user_id();
1714
        }
1715
1716
        if( 'course' != get_post_type( $course_id ) || ! get_userdata( $user_id )
1717
            || ! 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...
1718
            return;
1719
        }
1720
        $percentage_completed = $this->get_completion_percentage( $course_id, $user_id );
1721
1722
        echo $this->get_progress_meter( $percentage_completed );
1723
1724
    }// end the_progress_meter
1725
1726
    /**
1727
     * Checks how many lessons are completed
1728
     *
1729
     * @since 1.8.0
1730
     *
1731
     * @param int $course_id
1732
     * @param int $user_id
1733
     * @return array $completed_lesson_ids
1734
     */
1735
    public function get_completed_lesson_ids( $course_id, $user_id = 0 ){
1736
1737
        if( !( intval( $user_id ) ) > 0 ){
1738
            $user_id = get_current_user_id();
1739
        }
1740
1741
        $completed_lesson_ids = array();
1742
1743
        $course_lessons = $this->course_lessons( $course_id );
1744
1745
        foreach( $course_lessons as $lesson ){
1746
1747
            $is_lesson_completed = Sensei_Utils::user_completed_lesson( $lesson->ID, $user_id );
1748
            if( $is_lesson_completed ){
1749
                $completed_lesson_ids[] = $lesson->ID;
1750
            }
1751
1752
        }
1753
1754
        return $completed_lesson_ids;
1755
1756
    }// end get_completed_lesson_ids
1757
1758
    /**
1759
     * Calculate the perceantage completed in the course
1760
     *
1761
     * @since 1.8.0
1762
     *
1763
     * @param int $course_id
1764
     * @param int $user_id
1765
     * @return int $percentage
1766
     */
1767
    public function get_completion_percentage( $course_id, $user_id = 0 ){
1768
1769
        if( !( intval( $user_id ) ) > 0 ){
1770
            $user_id = get_current_user_id();
1771
        }
1772
1773
        $completed = count( $this->get_completed_lesson_ids( $course_id, $user_id ) );
1774
1775
        if( ! (  $completed  > 0 ) ){
1776
            return 0;
1777
        }
1778
1779
        $total_lessons = count( $this->course_lessons( $course_id ) );
1780
        $percentage = $completed / $total_lessons * 100;
1781
1782
        /**
1783
         *
1784
         * Filter the percentage returned for a users course.
1785
         *
1786
         * @param $percentage
1787
         * @param $course_id
1788
         * @param $user_id
1789
         * @since 1.8.0
1790
         */
1791
        return apply_filters( 'sensei_course_completion_percentage', $percentage, $course_id, $user_id );
1792
1793
    }// end get_completed_lesson_ids
1794
1795
    /**
1796
     * Block email notifications for the specific courses
1797
     * that the user disabled the notifications.
1798
     *
1799
     * @since 1.8.0
1800
     * @param $should_send
1801
     * @return bool
1802
     */
1803
    public function block_notification_emails( $should_send ){
1804
        global $sensei_email_data;
1805
        $email = $sensei_email_data;
1806
1807
        $course_id = '';
1808
1809
        if( isset( $email['course_id'] ) ){
1810
1811
            $course_id = $email['course_id'];
1812
1813
        }elseif( isset( $email['lesson_id'] ) ){
1814
1815
            $course_id = Sensei()->lesson->get_course_id( $email['lesson_id'] );
1816
1817
        }elseif( isset( $email['quiz_id'] ) ){
1818
1819
            $lesson_id = Sensei()->quiz->get_lesson_id( $email['quiz_id'] );
1820
            $course_id = Sensei()->lesson->get_course_id( $lesson_id );
1821
1822
        }
1823
1824
        if( !empty( $course_id ) && 'course'== get_post_type( $course_id ) ) {
1825
1826
            $course_emails_disabled = get_post_meta($course_id, 'disable_notification', true);
1827
1828
            if ($course_emails_disabled) {
1829
1830
                return false;
1831
1832
            }
1833
1834
        }// end if
1835
1836
        return $should_send;
1837
    }// end block_notification_emails
1838
1839
    /**
1840
     * Render the course notification setting meta box
1841
     *
1842
     * @since 1.8.0
1843
     * @param $course
1844
     */
1845
    public function course_notification_meta_box_content( $course ){
1846
1847
        $checked = get_post_meta( $course->ID , 'disable_notification', true );
1848
1849
        // generate checked html
1850
        $checked_html = '';
1851
        if( $checked ){
1852
            $checked_html = 'checked="checked"';
1853
        }
1854
        wp_nonce_field( 'update-course-notification-setting','_sensei_course_notification' );
1855
1856
        echo '<input id="disable_sensei_course_notification" '.$checked_html .' type="checkbox" name="disable_sensei_course_notification" >';
1857
        echo '<label for="disable_sensei_course_notification">'.__('Disable notifications on this course ?', 'woothemes-sensei'). '</label>';
1858
1859
    }// end course_notification_meta_box_content
1860
1861
    /**
1862
     * Store the setting for the course notification setting.
1863
     *
1864
     * @hooked int save_post
1865
     * @since 1.8.0
1866
     *
1867
     * @param $course_id
1868
     */
1869
    public function save_course_notification_meta_box( $course_id ){
1870
1871
        if( !isset( $_POST['_sensei_course_notification']  )
1872
            || ! wp_verify_nonce( $_POST['_sensei_course_notification'], 'update-course-notification-setting' ) ){
1873
            return;
1874
        }
1875
1876
        if( isset( $_POST['disable_sensei_course_notification'] ) && 'on'== $_POST['disable_sensei_course_notification']  ) {
1877
            $new_val = true;
1878
        }else{
1879
            $new_val = false;
1880
        }
1881
1882
       update_post_meta( $course_id , 'disable_notification', $new_val );
1883
1884
    }// end save notification meta box
1885
1886
    /**
1887
     * Backwards compatibility hooks added to ensure that
1888
     * plugins and other parts of sensei still works.
1889
     *
1890
     * This function hooks into `sensei_course_content_inside_before`
1891
     *
1892
     * @since 1.9
1893
     *
1894
     * @param WP_Post $post
0 ignored issues
show
Bug introduced by
There is no parameter named $post. 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...
1895
     */
1896
    public function content_before_backwards_compatibility_hooks( $post_id ){
0 ignored issues
show
Unused Code introduced by
The parameter $post_id 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...
1897
1898
        sensei_do_deprecated_action( 'sensei_course_image','1.9.0','sensei_course_content_inside_before' );
1899
        sensei_do_deprecated_action( 'sensei_course_archive_course_title','1.9.0','sensei_course_content_inside_before' );
1900
1901
    }
1902
1903
    /**
1904
     * Backwards compatibility hooks that should be hooked into sensei_loop_course_before
1905
     *
1906
     * hooked into 'sensei_loop_course_before'
1907
     *
1908
     * @since 1.9
1909
     *
1910
     * @global WP_Post $post
1911
     */
1912
    public  function loop_before_backwards_compatibility_hooks( ){
1913
1914
        global $post;
1915
        sensei_do_deprecated_action( 'sensei_course_archive_header','1.9.0','sensei_course_content_inside_before', $post->post_type  );
1916
1917
    }
1918
1919
    /**
1920
     * Output a link to view course. The button text is different depending on the amount of preview lesson available.
1921
     *
1922
     * hooked into 'sensei_course_content_inside_after'
1923
     *
1924
     * @since 1.9.0
1925
     *
1926
     * @param integer $course_id
1927
     */
1928
    public function the_course_free_lesson_preview( $course_id ){
1929
        // Meta data
1930
        $course = get_post( $course_id );
1931
        $preview_lesson_count = intval( Sensei()->course->course_lesson_preview_count( $course->ID ) );
1932
        $is_user_taking_course = Sensei_Utils::user_started_course( $course->ID, get_current_user_id() );
1933
1934 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...
1935
            ?>
1936
            <p class="sensei-free-lessons">
1937
                <a href="<?php echo get_permalink(); ?>">
1938
                    <?php _e( 'Preview this course', 'woothemes-sensei' ) ?>
1939
                </a>
1940
                - <?php echo sprintf( __( '(%d preview lessons)', 'woothemes-sensei' ), $preview_lesson_count ) ; ?>
1941
            </p>
1942
1943
        <?php
1944
        }
1945
    }
1946
1947
    /**
1948
     * Add course mata to the course meta hook
1949
     *
1950
     * @since 1.9.0
1951
     * @param integer $course_id
1952
     */
1953
    public function the_course_meta( $course_id ){
1954
        echo '<p class="sensei-course-meta">';
1955
1956
        $course = get_post( $course_id );
1957
        $category_output = get_the_term_list( $course->ID, 'course-category', '', ', ', '' );
1958
        $author_display_name = get_the_author_meta( 'display_name', $course->post_author  );
1959
1960
        if ( isset( Sensei()->settings->settings[ 'course_author' ] ) && ( Sensei()->settings->settings[ 'course_author' ] ) ) {?>
1961
1962
            <span class="course-author"><?php _e( 'by ', 'woothemes-sensei' ); ?>
1963
1964
                <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>
1965
1966
            </span>
1967
1968
        <?php } // End If Statement ?>
1969
1970
        <span class="course-lesson-count"><?php echo Sensei()->course->course_lesson_count( $course->ID ) . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' ); ?></span>
1971
1972
       <?php if ( '' != $category_output ) { ?>
1973
1974
            <span class="course-category"><?php echo sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ); ?></span>
1975
1976
        <?php } // End If Statement
1977
1978
        // number of completed lessons
1979
        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...
1980
            || Sensei_Utils::user_completed_course( $course->ID,  get_current_user_id() )  ){
1981
1982
            $completed = count( $this->get_completed_lesson_ids( $course->ID, get_current_user_id() ) );
1983
            $lesson_count = count( $this->course_lessons( $course->ID ) );
1984
            echo '<span class="course-lesson-progress">' . sprintf( __( '%1$d of %2$d lessons completed', 'woothemes-sensei' ) , $completed, $lesson_count  ) . '</span>';
1985
1986
        }
1987
1988
        sensei_simple_course_price( $course->ID );
1989
1990
        echo '</p>';
1991
    } // end the course meta
1992
1993
    /**
1994
     * Filter the classes attached to a post types for courses
1995
     * and add a status class for when the user is logged in.
1996
     *
1997
     * @param $classes
1998
     * @param $class
1999
     * @param $post_id
2000
     *
2001
     * @return array $classes
2002
     */
2003
    public static function add_course_user_status_class( $classes, $class, $course_id ){
2004
2005
        if( 'course' == get_post_type( $course_id )  &&  is_user_logged_in() ){
2006
2007
            if( Sensei_Utils::user_completed_course( $course_id, get_current_user_id() ) ){
2008
2009
                $classes[] = 'user-status-completed';
2010
2011
            }else{
2012
2013
                $classes[] = 'user-status-active';
2014
2015
            }
2016
2017
        }
2018
2019
        return $classes;
2020
2021
    }// end add_course_user_status_class
2022
2023
    /**
2024
     * Prints out the course action buttons links
2025
     *
2026
     * - complete course
2027
     * - delete course
2028
     *
2029
     * @param WP_Post $course
2030
     */
2031
    public static function the_course_action_buttons( $course ){
2032
2033
        if( is_user_logged_in() ) { ?>
2034
2035
            <section class="entry-actions">
2036
                <form method="POST" action="<?php  echo esc_url( remove_query_arg( array( 'active_page', 'completed_page' ) ) ); ?>">
2037
2038
                    <input type="hidden"
2039
                           name="<?php esc_attr_e( 'woothemes_sensei_complete_course_noonce' ) ?>"
2040
                           id="<?php  esc_attr_e( 'woothemes_sensei_complete_course_noonce' ); ?>"
2041
                           value="<?php esc_attr_e( wp_create_nonce( 'woothemes_sensei_complete_course_noonce' ) ); ?>"
2042
                        />
2043
2044
                    <input type="hidden" name="course_complete_id" id="course-complete-id" value="<?php esc_attr_e( intval( $course->ID ) ); ?>" />
2045
2046
                    <?php if ( 0 < absint( count( Sensei()->course->course_lessons( $course->ID ) ) )
2047
                        && Sensei()->settings->settings['course_completion'] == 'complete'
2048
                        && ! Sensei_Utils::user_completed_course( $course, get_current_user_id() )) { ?>
2049
2050
                        <span><input name="course_complete" type="submit" class="course-complete" value="<?php  _e( 'Mark as Complete', 'woothemes-sensei' ); ?>" /></span>
2051
2052
                   <?php  } // End If Statement
2053
2054
                    $course_purchased = false;
2055
                    if ( Sensei_WC::is_woocommerce_active() ) {
2056
                        // Get the product ID
2057
                        $wc_post_id = get_post_meta( intval( $course->ID ), '_course_woocommerce_product', true );
2058
                        if ( 0 < $wc_post_id ) {
2059
2060
                            $user = wp_get_current_user();
2061
                            $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...
2062
2063
                        } // End If Statement
2064
                    } // End If Statement
2065
2066
                    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...
2067
2068
                        <span><input name="course_complete" type="submit" class="course-delete" value="<?php echo __( 'Delete Course', 'woothemes-sensei' ); ?>"/></span>
2069
2070
                    <?php } // End If Statement
2071
2072
                    $has_quizzes = Sensei()->course->course_quizzes( $course->ID, true );
2073
                    $results_link = '';
2074 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...
2075
                        $results_link = '<a class="button view-results" href="' . Sensei()->course_results->get_permalink( $course->ID ) . '">' . __( 'View results', 'woothemes-sensei' ) . '</a>';
2076
                    }
2077
2078
                    // Output only if there is content to display
2079
                    if ( has_filter( 'sensei_results_links' ) || $has_quizzes ) { ?>
2080
2081
                        <p class="sensei-results-links">
2082
                            <?php
2083
                            /**
2084
                             * Filter the results links
2085
                             *
2086
                             * @param string $results_links_html
2087
                             * @param integer $course_id
2088
                             */
2089
                            echo apply_filters( 'sensei_results_links', $results_link, $course->ID );
2090
                            ?>
2091
                        </p>
2092
2093
                    <?php } // end if has filter  ?>
2094
                </form>
2095
            </section>
2096
2097
        <?php  }// end if is user logged in
2098
2099
    }// end the_course_action_buttons
2100
2101
    /**
2102
     * This function alter the main query on the course archive page.
2103
     * This also gives Sensei specific filters that allows variables to be altered specifically on the course archive.
2104
     *
2105
     * This function targets only the course archives and the my courses page. Shortcodes can set their own
2106
     * query parameters via the arguments.
2107
     *
2108
     * This function is hooked into pre_get_posts filter
2109
     *
2110
     * @since 1.9.0
2111
     *
2112
     * @param WP_Query $query
2113
     * @return WP_Query $query
2114
     */
2115
    public static function course_query_filter( $query ){
2116
2117
        // exit early for no course queries and admin queries
2118
        if( is_admin( ) || 'course' != $query->get( 'post_type' ) ){
2119
            return $query;
2120
        }
2121
2122
        global $post; // used to get the current page id for my courses
2123
2124
        // for the course archive page
2125
        if( $query->is_main_query() && is_post_type_archive('course') )
2126
        {
2127
            /**
2128
             * sensei_archive_courses_per_page
2129
             *
2130
             * Sensei courses per page on the course
2131
             * archive
2132
             *
2133
             * @since 1.9.0
2134
             * @param integer $posts_per_page default 10
2135
             */
2136
            $query->set( 'posts_per_page', apply_filters( 'sensei_archive_courses_per_page', 10 ) );
2137
2138
        }
2139
        // for the my courses page
2140
        elseif( is_page() && Sensei()->settings->get( 'my_course_page' ) == $post->ID  )
2141
        {
2142
            /**
2143
             * sensei_my_courses_per_page
2144
             *
2145
             * Sensei courses per page on the my courses page
2146
             * as set in the settings
2147
             *
2148
             * @since 1.9.0
2149
             * @param integer $posts_per_page default 10
2150
             */
2151
            $query->set( 'posts_per_page', apply_filters( 'sensei_my_courses_per_page', 10 ) );
2152
2153
        }
2154
2155
        return $query;
2156
2157
    }// end course_query_filter
2158
2159
    /**
2160
     * Determine the class of the course loop
2161
     *
2162
     * This will output .first or .last and .course-item-number-x
2163
     *
2164
     * @return array $extra_classes
2165
     * @since 1.9.0
2166
     */
2167
    public static function get_course_loop_content_class ()
2168
    {
2169
2170
        global $sensei_course_loop;
2171
2172
2173
        if( !isset( $sensei_course_loop ) ){
2174
            $sensei_course_loop = array();
2175
        }
2176
2177
        if (!isset($sensei_course_loop['counter'])) {
2178
            $sensei_course_loop['counter'] = 0;
2179
        }
2180
2181
        if (!isset($sensei_course_loop['columns'])) {
2182
            $sensei_course_loop['columns'] = self::get_loop_number_of_columns();
2183
        }
2184
2185
        // increment the counter
2186
        $sensei_course_loop['counter']++;
2187
2188
        $extra_classes = array();
2189
        if( 0 == ( $sensei_course_loop['counter'] - 1 ) % $sensei_course_loop['columns'] || 1 == $sensei_course_loop['columns']  ){
2190
            $extra_classes[] = 'first';
2191
        }
2192
2193
        if( 0 == $sensei_course_loop['counter'] % $sensei_course_loop['columns']  ){
2194
            $extra_classes[] = 'last';
2195
        }
2196
2197
        // add the item number to the classes as well.
2198
        $extra_classes[] = 'loop-item-number-'. $sensei_course_loop['counter'];
2199
2200
        /**
2201
         * Filter the course loop class the fires in the  in get_course_loop_content_class function
2202
         * which is called from the course loop content-course.php
2203
         *
2204
         * @since 1.9.0
2205
         *
2206
         * @param array $extra_classes
2207
         * @param WP_Post $loop_current_course
2208
         */
2209
        return apply_filters( 'sensei_course_loop_content_class', $extra_classes ,get_post() );
2210
2211
    }// end get_course_loop_class
2212
2213
    /**
2214
     * Get the number of columns set for Sensei courses
2215
     *
2216
     * @since 1.9.0
2217
     * @return mixed|void
2218
     */
2219
    public static function get_loop_number_of_columns(){
2220
2221
        /**
2222
         * Filter the number of columns on the course archive page.
2223
         *
2224
         * @since 1.9.0
2225
         * @param int $number_of_columns default 1
2226
         */
2227
        return apply_filters('sensei_course_loop_number_of_columns', 1);
2228
2229
    }
2230
2231
    /**
2232
     * Output the course archive filter markup
2233
     *
2234
     * hooked into sensei_loop_course_before
2235
     *
2236
     * @since 1.9.0
2237
     * @param
2238
     */
2239
    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...
2240
2241
        // don't show on category pages and other pages
2242
        if( ! is_archive(  'course ') || is_tax('course-category') ){
2243
            return;
2244
        }
2245
2246
        /**
2247
         * Filter the sensei archive course order by values
2248
         *
2249
         * @since 1.9.0
2250
         * @param array $options {
2251
         *  @type string $option_value
2252
         *  @type string $option_string
2253
         * }
2254
         */
2255
        $course_order_by_options = apply_filters( 'sensei_archive_course_order_by_options', array(
2256
            "newness"     => __( "Sort by newest first", "woothemes-sensei"),
2257
            "title"       => __( "Sort by title A-Z", "woothemes-sensei" ),
2258
        ));
2259
2260
        // setup the currently selected item
2261
        $selected = 'newness';
2262
        if( isset( $_GET['orderby'] ) ){
2263
2264
            $selected =  $_GET[ 'orderby' ];
2265
2266
        }
2267
2268
        ?>
2269
2270
        <form class="sensei-ordering" name="sensei-course-order" action="<?php echo esc_attr( Sensei_Utils::get_current_url() ) ; ?>" method="POST">
2271
            <select name="course-orderby" class="orderby">
2272
                <?php
2273
                foreach( $course_order_by_options as $value => $text ){
2274
2275
                    echo '<option value="'. $value . ' "' . selected( $selected, $value, false ) . '>'. $text. '</option>';
2276
2277
                }
2278
                ?>
2279
            </select>
2280
        </form>
2281
2282
    <?php
2283
    }// end course archive filters
2284
2285
    /**
2286
     * Output the course archive filter markup
2287
     *
2288
     * hooked into sensei_loop_course_before
2289
     *
2290
     * @since 1.9.0
2291
     * @param
2292
     */
2293
    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...
2294
2295
        // don't show on category pages
2296
        if( is_tax('course-category') ){
2297
            return;
2298
        }
2299
2300
        /**
2301
         * filter the course archive filter buttons
2302
         *
2303
         * @since 1.9.0
2304
         * @param array $filters{
2305
         *   @type array ( $id, $url , $title )
2306
         * }
2307
         *
2308
         */
2309
        $filters = apply_filters( 'sensei_archive_course_filter_by_options', array(
2310
            array( 'id' => 'all', 'url' => self::get_courses_page_url(), 'title'=> __( 'All', 'woothemes-sensei' ) ),
2311
            array( 'id' => 'featured', 'url' => add_query_arg( array( 'course_filter'=>'featured'), self::get_courses_page_url()  ), 'title'=> __( 'Featured', 'woothemes-sensei' ) ),
2312
        ));
2313
2314
2315
        ?>
2316
        <ul class="sensei-course-filters clearfix" >
2317
            <?php
2318
2319
            //determine the current active url
2320
            $current_url = Sensei_Utils::get_current_url();
2321
2322
            foreach( $filters as $filter ) {
2323
2324
                $active_class =  $current_url == $filter['url'] ? ' class="active" ' : '';
2325
2326
                echo '<li><a '. $active_class .' id="'. $filter['id'] .'" href="'. esc_url( $filter['url'] ).'" >'. $filter['title']  .'</a></li>';
2327
2328
            }
2329
            ?>
2330
2331
        </ul>
2332
2333
        <?php
2334
2335
    }
2336
2337
    /**
2338
     * if the featured link is clicked on the course archive page
2339
     * filter the courses returned to only show those featured
2340
     *
2341
     * Hooked into pre_get_posts
2342
     *
2343
     * @since 1.9.0
2344
     * @param WP_Query $query
2345
     * @return WP_Query $query
2346
     */
2347
    public static function course_archive_featured_filter( $query ){
2348
2349
        if( isset ( $_GET[ 'course_filter' ] ) && 'featured'== $_GET['course_filter'] && $query->is_main_query()  ){
2350
            //setup meta query for featured courses
2351
            $query->set( 'meta_value', 'featured'  );
2352
            $query->set( 'meta_key', '_course_featured'  );
2353
            $query->set( 'meta_compare', '='  );
2354
        }
2355
2356
        return $query;
2357
    }
2358
2359
    /**
2360
     * if the course order drop down is changed
2361
     *
2362
     * Hooked into pre_get_posts
2363
     *
2364
     * @since 1.9.0
2365
     * @param WP_Query $query
2366
     * @return WP_Query $query
2367
     */
2368
    public static function course_archive_order_by_title( $query ){
2369
2370
        if( isset ( $_POST[ 'course-orderby' ] ) && 'title '== $_POST['course-orderby']
2371
            && 'course'== $query->get('post_type') && $query->is_main_query()  ){
2372
            // setup the order by title for this query
2373
            $query->set( 'orderby', 'title'  );
2374
            $query->set( 'order', 'ASC'  );
2375
        }
2376
2377
        return $query;
2378
    }
2379
2380
2381
    /**
2382
     * Get the link to the courses page. This will be the course post type archive
2383
     * page link or the page the user set in their settings
2384
     *
2385
     * @since 1.9.0
2386
     * @return string $course_page_url
2387
     */
2388
    public static function get_courses_page_url(){
2389
2390
        $course_page_id = intval( Sensei()->settings->settings[ 'course_page' ] );
2391
        $course_page_url = empty( $course_page_id ) ? get_post_type_archive_link('course') : get_permalink( $course_page_id );
2392
2393
        return $course_page_url;
2394
2395
    }// get_course_url
2396
2397
    /**
2398
     * Output the headers on the course archive page
2399
     *
2400
     * Hooked into the sensei_archive_title
2401
     *
2402
     * @since 1.9.0
2403
     * @param string $query_type
2404
     * @param string $before_html
2405
     * @param string $after_html
2406
     * @return void
2407
     */
2408
    public static function archive_header( $query_type ='' , $before_html='', $after_html =''  ){
2409
2410
        if( ! is_post_type_archive('course') ){
2411
            return;
2412
        }
2413
2414
        // deprecated since 1.9.0
2415
        sensei_do_deprecated_action('sensei_archive_title','1.9.0','sensei_archive_before_course_loop');
2416
2417
        $html = '';
2418
2419
        if( empty( $before_html ) ){
2420
2421
            $before_html = '<header class="archive-header"><h1>';
2422
2423
        }
2424
2425
        if( empty( $after_html ) ){
2426
2427
            $after_html = '</h1></header>';
2428
2429
        }
2430
2431
        if ( is_tax( 'course-category' ) ) {
2432
2433
            global $wp_query;
2434
2435
            $taxonomy_obj = $wp_query->get_queried_object();
2436
            $taxonomy_short_name = $taxonomy_obj->taxonomy;
2437
            $taxonomy_raw_obj = get_taxonomy( $taxonomy_short_name );
2438
            $title = sprintf( __( '%1$s Archives: %2$s', 'woothemes-sensei' ), $taxonomy_raw_obj->labels->name, $taxonomy_obj->name );
2439
            echo apply_filters( 'course_category_archive_title', $before_html . $title . $after_html );
2440
            return;
2441
2442
        } // End If Statement
2443
2444
        switch ( $query_type ) {
2445
            case 'newcourses':
2446
                $html .= $before_html . __( 'New Courses', 'woothemes-sensei' ) . $after_html;
2447
                break;
2448
            case 'featuredcourses':
2449
                $html .= $before_html .  __( 'Featured Courses', 'woothemes-sensei' ) . $after_html;
2450
                break;
2451
            case 'freecourses':
2452
                $html .= $before_html .  __( 'Free Courses', 'woothemes-sensei' ) . $after_html;
2453
                break;
2454
            case 'paidcourses':
2455
                $html .= $before_html .  __( 'Paid Courses', 'woothemes-sensei' ) . $after_html;
2456
                break;
2457
            default:
2458
                $html .= $before_html . __( 'Courses', 'woothemes-sensei' ) . $after_html;
2459
                break;
2460
        } // End Switch Statement
2461
2462
        echo apply_filters( 'course_archive_title', $html );
2463
2464
    }//course_archive_header
2465
2466
2467
    /**
2468
     * Filter the single course content
2469
     * taking into account if the user has access.
2470
     *
2471
     * @1.9.0
2472
     *
2473
     * @param string $content
2474
     * @return string $content or $excerpt
2475
     */
2476
    public static function single_course_content( $content ){
2477
2478
        if( ! is_singular('course') ){
2479
2480
            return $content;
2481
2482
        }
2483
2484
        // Content Access Permissions
2485
        $access_permission = false;
2486
2487
        if ( ! Sensei()->settings->get('access_permission')  || sensei_all_access() ) {
2488
2489
            $access_permission = true;
2490
2491
        } // End If Statement
2492
2493
        // Check if the user is taking the course
2494
        $is_user_taking_course = Sensei_Utils::user_started_course( get_the_ID(), get_current_user_id() );
2495
2496
        if(Sensei_WC::is_woocommerce_active()) {
2497
2498
            $wc_post_id = get_post_meta( get_the_ID(), '_course_woocommerce_product', true );
2499
            $product = Sensei()->sensei_get_woocommerce_product_object( $wc_post_id );
0 ignored issues
show
Deprecated Code introduced by
The method Sensei_Main::sensei_get_...mmerce_product_object() has been deprecated with message: since 1.9.0

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...
2500
2501
            $has_product_attached = isset ( $product ) && is_object ( $product );
2502
2503
        } else {
2504
2505
            $has_product_attached = false;
2506
2507
        }
2508
2509
        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...
2510
            || ( $access_permission && !$has_product_attached)
2511
            || 'full' == Sensei()->settings->get( 'course_single_content_display' ) ) {
2512
2513
	        // compensate for core providing and empty $content
2514
2515
	        if( empty( $content ) ){
2516
		        remove_filter( 'the_content', array( 'Sensei_Course', 'single_course_content') );
2517
		        $course = get_post( get_the_ID() );
2518
2519
		        $content = apply_filters( 'the_content', $course->post_content );
2520
2521
	        }
2522
2523
            return $content;
2524
2525
        } else {
2526
2527
            return '<p class="course-excerpt">' . get_post(  get_the_ID() )->post_excerpt . '</p>';
2528
2529
        }
2530
2531
    }// end single_course_content
2532
2533
    /**
2534
     * Output the the single course lessons title with markup.
2535
     *
2536
     * @since 1.9.0
2537
     */
2538
    public static function the_course_lessons_title(){
2539
2540
	    if ( ! is_singular( 'course' )  ) {
2541
		    return;
2542
	    }
2543
2544
        global $post;
2545
        $none_module_lessons = Sensei()->modules->get_none_module_lessons( $post->ID  );
2546
        $course_lessons = Sensei()->course->course_lessons( $post->ID );
2547
2548
        // title should be Other Lessons if there are lessons belonging to models.
2549
        $title = __('Other Lessons', 'woothemes-sensei');
2550
2551
        // show lessons if the number of lesson in the course is the same as those that isn't assigned to a module
2552
        if( count( $course_lessons ) == count( $none_module_lessons )  ){
2553
2554
            $title = __('Lessons', 'woothemes-sensei');
2555
2556
        }elseif( empty( $none_module_lessons ) ){ // if the none module lessons are simply empty the title should not be shown
2557
2558
            $title = '';
2559
        }
2560
2561
        /**
2562
         * hook document in class-woothemes-sensei-message.php
2563
         */
2564
        $title = apply_filters( 'sensei_single_title', $title, $post->post_type );
2565
2566
        ob_start(); // start capturing the following output.
2567
2568
        ?>
2569
2570
            <header>
2571
                <h2> <?php echo $title; ?> </h2>
2572
            </header>
2573
2574
        <?php
2575
2576
        /**
2577
         * Filter the title and markup that appears above the lessons on a single course
2578
         * page.
2579
         *
2580
         * @since 1.9.0
2581
         * @param string $lessons_title_html
2582
         */
2583
        echo apply_filters('the_course_lessons_title', ob_get_clean() ); // output and filter the captured output and stop capturing.
2584
2585
    }// end the_course_lessons_title
2586
2587
    /**
2588
     * This function loads the global wp_query object with with lessons
2589
     * of the current course. It is designed to be used on the single-course template
2590
     * and expects the global post to be a singular course.
2591
     *
2592
     * This function excludes lessons belonging to modules as they are
2593
     * queried separately.
2594
     *
2595
     * @since 1.9.0
2596
     * @global $wp_query
2597
     */
2598
    public static function load_single_course_lessons_query(){
2599
2600
        global $post, $wp_query;
2601
2602
        $course_id = $post->ID;
2603
2604
        if( 'course' != get_post_type( $course_id ) ){
2605
            return;
2606
        }
2607
2608
        $course_lesson_query_args = array(
2609
            'post_type'         => 'lesson',
2610
            'posts_per_page'    => 500,
2611
            'orderby'           => 'date',
2612
            'order'             => 'ASC',
2613
            'meta_query'        => array(
2614
                array(
2615
                    'key' => '_lesson_course',
2616
                    'value' => intval( $course_id ),
2617
                ),
2618
            ),
2619
            'post_status'       => 'public',
2620
            'suppress_filters'  => 0,
2621
        );
2622
2623
        // Exclude lessons belonging to modules as they are queried along with the modules.
2624
        $modules = Sensei()->modules->get_course_modules( $course_id );
2625
        if( !is_wp_error( $modules ) && ! empty( $modules ) && is_array( $modules ) ){
2626
2627
            $terms_ids = array();
2628
            foreach( $modules as $term ){
2629
2630
                $terms_ids[] = $term->term_id;
2631
2632
            }
2633
2634
            $course_lesson_query_args[ 'tax_query'] = array(
2635
                array(
2636
                    'taxonomy' => 'module',
2637
                    'field'    => 'id',
2638
                    'terms'    => $terms_ids,
2639
                    'operator' => 'NOT IN',
2640
                ),
2641
            );
2642
        }
2643
2644
        //setting lesson order
2645
        $course_lesson_order = get_post_meta( $course_id, '_lesson_order', true);
2646
        if( !empty( $course_lesson_order ) ){
2647
2648
            $course_lesson_query_args['post__in'] = explode( ',', $course_lesson_order );
2649
            $course_lesson_query_args['orderby']= 'post__in' ;
2650
            unset( $course_lesson_query_args['order'] );
2651
2652
        }
2653
2654
        $wp_query = new WP_Query( $course_lesson_query_args );
2655
2656
    }// load_single_course_lessons
2657
2658
    /**
2659
     * Flush the rewrite rules for a course post type
2660
     *
2661
     * @since 1.9.0
2662
     *
2663
     * @param $post_id
2664
     */
2665 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...
2666
2667
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
2668
2669
            return;
2670
2671
        }
2672
2673
2674
        if( 'course' == get_post_type( $post_id )  ){
2675
2676
            Sensei()->initiate_rewrite_rules_flush();
2677
2678
        }
2679
2680
    }
2681
2682
    /**
2683
     * Optionally return the full content on the single course pages
2684
     * depending on the users course_single_content_display setting
2685
     *
2686
     * @since 1.9.0
2687
     * @param $excerpt
2688
     * @return string
2689
     */
2690
    public static function full_content_excerpt_override( $excerpt ){
2691
2692
        if (   is_singular('course')  &&
2693
                'full' == Sensei()->settings->get( 'course_single_content_display' ) ){
2694
2695
            return get_the_content();
2696
2697
        } else {
2698
2699
            return $excerpt;
2700
2701
        }
2702
2703
    }
2704
2705
    /**
2706
     * Output the course actions like start taking course, register, add to cart etc.
2707
     *
2708
     * @since 1.9.0
2709
     */
2710
    public static function the_course_enrolment_actions(){
2711
        ?>
2712
        <section class="course-meta course-enrolment">
2713
        <?php
2714
        global  $post, $current_user;
2715
        $is_user_taking_course = Sensei_Utils::user_started_course( $post->ID, $current_user->ID );
2716
        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...
2717
2718
            // Get the product ID
2719
            $wc_post_id = absint( get_post_meta( $post->ID, '_course_woocommerce_product', true ) );
2720
2721
            // Check for woocommerce
2722
            if ( Sensei_WC::is_woocommerce_active() && ( 0 < intval( $wc_post_id ) ) ) {
2723
                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...
2724
            } else {
2725
                sensei_start_course_form($post->ID);
2726
            } // End If Statement
2727
2728
        } elseif ( is_user_logged_in() ) {
2729
2730
            // Check if course is completed
2731
            $user_course_status = Sensei_Utils::user_course_status( $post->ID, $current_user->ID );
2732
            $completed_course = Sensei_Utils::user_completed_course( $user_course_status );
2733
            // Success message
2734
            if ( $completed_course ) { ?>
2735
                <div class="status completed"><?php  _e( 'Completed', 'woothemes-sensei' ); ?></div>
2736
                <?php
2737
                $has_quizzes = Sensei()->course->course_quizzes( $post->ID, true );
2738
                if( has_filter( 'sensei_results_links' ) || $has_quizzes ) { ?>
2739
                    <p class="sensei-results-links">
2740
                        <?php
2741
                        $results_link = '';
2742 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...
2743
                            $results_link = '<a class="view-results" href="' . Sensei()->course_results->get_permalink( $post->ID ) . '">' .  __( 'View results', 'woothemes-sensei' ) . '</a>';
2744
                        }
2745
                        /**
2746
                         * Filter documented in Sensei_Course::the_course_action_buttons
2747
                         */
2748
                        $results_link = apply_filters( 'sensei_results_links', $results_link, $post->ID );
2749
                        echo $results_link;
2750
                        ?></p>
2751
                <?php } ?>
2752
            <?php } else { ?>
2753
                <div class="status in-progress"><?php echo __( 'In Progress', 'woothemes-sensei' ); ?></div>
2754
            <?php }
2755
2756
        } else {
2757
            // Get the product ID
2758
            $wc_post_id = absint( get_post_meta( $post->ID, '_course_woocommerce_product', true ) );
2759
            // Check for woocommerce
2760
            if ( Sensei_WC::is_woocommerce_active() && ( 0 < intval( $wc_post_id ) ) ) {
2761
2762
                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...
2763
2764
            } else {
2765
2766
                if( get_option( 'users_can_register') ) {
2767
2768
2769
                    $my_courses_page_id = '';
2770
2771
                    /**
2772
                     * Filter to force Sensei to output the default WordPress user
2773
                     * registration link.
2774
                     *
2775
                     * @since 1.9.0
2776
                     * @param bool $wp_register_link default false
2777
                     */
2778
2779
                    $wp_register_link = apply_filters('sensei_use_wp_register_link', false);
2780
2781
                    $settings = Sensei()->settings->get_settings();
2782 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...
2783
                        && 0 < intval( $settings[ 'my_course_page' ] ) ){
2784
2785
                        $my_courses_page_id = $settings[ 'my_course_page' ];
2786
2787
                    }
2788
2789
                    // If a My Courses page was set in Settings, and 'sensei_use_wp_register_link'
2790
                    // is false, link to My Courses. If not, link to default WordPress registration page.
2791
                    if( !empty( $my_courses_page_id ) && $my_courses_page_id && !$wp_register_link){
2792
2793
                        $my_courses_url = get_permalink( $my_courses_page_id  );
2794
                        $register_link = '<a href="'.$my_courses_url. '">' . __('Register', 'woothemes-sensei') .'</a>';
2795
                        echo '<div class="status register">' . $register_link . '</div>' ;
2796
2797
                    } else{
2798
2799
                        wp_register( '<div class="status register">', '</div>' );
2800
2801
                    }
2802
2803
                } // end if user can register
2804
2805
            } // End If Statement
2806
2807
        } // End If Statement ?>
2808
2809
        </section><?php
2810
2811
    }// end the_course_enrolment_actions
2812
2813
    /**
2814
     * Output the course video inside the loop.
2815
     *
2816
     * @since 1.9.0
2817
     */
2818 View Code Duplication
    public static function the_course_video(){
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...
2819
2820
        global $post;
2821
2822
	    if ( ! is_singular( 'course' )  ) {
2823
		    return;
2824
	    }
2825
        // Get the meta info
2826
        $course_video_embed = get_post_meta( $post->ID, '_course_video_embed', true );
2827
2828
        if ( 'http' == substr( $course_video_embed, 0, 4) ) {
2829
2830
            $course_video_embed = wp_oembed_get( esc_url( $course_video_embed ) );
2831
2832
        } // End If Statement
2833
2834
        if ( '' != $course_video_embed ) { ?>
2835
2836
            <div class="course-video">
2837
                <?php echo html_entity_decode($course_video_embed); ?>
2838
            </div>
2839
2840
        <?php } // End If Statement
2841
    }
2842
2843
    /**
2844
     * Output the title for the single lesson page
2845
     *
2846
     * @global $post
2847
     * @since 1.9.0
2848
     */
2849
    public static function the_title(){
2850
2851
	    if( ! is_singular( 'course' ) ){
2852
			return;
2853
	    }
2854
        global $post;
2855
2856
        ?>
2857
        <header>
2858
2859
            <h1>
2860
2861
                <?php
2862
                /**
2863
                 * Filter documented in class-sensei-messages.php the_title
2864
                 */
2865
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
2866
                ?>
2867
2868
            </h1>
2869
2870
        </header>
2871
2872
        <?php
2873
2874
    }//the_title
2875
2876
    /**
2877
     * Show the title on the course category pages
2878
     *
2879
     * @since 1.9.0
2880
     */
2881
    public static function course_category_title(){
2882
2883
        if( ! is_tax( 'course-category' ) ){
2884
            return;
2885
        }
2886
2887
        $category_slug = get_query_var('course-category');
2888
        $term  = get_term_by('slug',$category_slug,'course-category');
2889
2890
        if( ! empty($term) ){
2891
2892
            $title = $term->name;
2893
2894
        }else{
2895
2896
            $title = 'Course Category';
2897
2898
        }
2899
2900
        $html = '<h2 class="sensei-category-title">';
2901
        $html .= __('Category') . ' ' . $title;
2902
        $html .= '</h2>';
2903
2904
        echo apply_filters( 'course_category_title', $html , $term->term_id );
2905
2906
    }// course_category_title
2907
2908
    /**
2909
     * Alter the course query to respect the order set for courses and apply
2910
     * this on the course-category pages.
2911
     *
2912
     * @since 1.9.0
2913
     *
2914
     * @param WP_Query $query
2915
     * @return WP_Query
2916
     */
2917
    public static function alter_course_category_order( $query ){
2918
2919
        if( ! is_tax( 'course-category' ) || ! $query->is_main_query() ){
2920
            return $query;
2921
        }
2922
2923
        $order = get_option( 'sensei_course_order', '' );
2924
        if( !empty( $order )  ){
2925
            $query->set('orderby', 'menu_order' );
2926
            $query->set('order', 'ASC' );
2927
        }
2928
2929
        return $query;
2930
2931
    }
2932
2933
    /**
2934
     * The very basic course query arguments
2935
     * so we don't have to repeat this througout
2936
     * the code base.
2937
     *
2938
     * Usage:
2939
     * $args = Sensei_Course::get_default_query_args();
2940
     * $args['custom_arg'] ='custom value';
2941
     * $courses = get_posts( $args )
2942
     *
2943
     * @since 1.9.0
2944
     *
2945
     * @return array
2946
     */
2947
    public static function get_default_query_args(){
2948
        return array(
2949
            'post_type' 		=> 'course',
2950
            'posts_per_page' 		=> 1000,
2951
            'orderby'         	=> 'date',
2952
            'order'           	=> 'DESC',
2953
            'suppress_filters' 	=> 0
2954
        );
2955
    }
2956
2957
    /**
2958
     * Check if the prerequisite course is completed
2959
     * Courses with no pre-requisite should always return true
2960
     *
2961
     * @since 1.9.0
2962
     * @param $course_id
2963
     * @return bool
2964
     */
2965
    public static function is_prerequisite_complete( $course_id ){
2966
2967
        $course_prerequisite_id = get_post_meta( $course_id, '_course_prerequisite', true );
2968
2969
        // if it has a pre requisite course check it
2970
        if( ! empty(  $course_prerequisite_id ) ){
2971
2972
            return Sensei_Utils::user_completed_course( $course_prerequisite_id, get_current_user_id() );
2973
2974
        }
2975
2976
        return true;
2977
2978
    }// end is_prerequisite_complete
2979
2980
2981
}// End Class
2982
2983
/**
2984
 * Class WooThemes_Sensei_Course
2985
 * @ignore only for backward compatibility
2986
 * @since 1.9.0
2987
 */
2988
class WooThemes_Sensei_Course extends Sensei_Course{}
2989