Issues (896)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/class-sensei-course.php (27 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 2.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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' ) ) {
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
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
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
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
		if ( isset($this->meta_fields) && is_array($this->meta_fields) ) {
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
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" />';
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
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
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
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
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
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
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
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
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
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 );
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
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
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
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
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' ] ) ) ) {
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
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
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
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
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' ] )
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
	                /**
1334
	                 * documented in class-sensei-course.php the_course_action_buttons function
1335
	                 */
1336
	                $show_delete_course_button = apply_filters( 'sensei_show_delete_course_button', false );
1337
1338
                    if ( false == $course_purchased && $show_delete_course_button ) {
1339
1340
                        $active_html .= '<span><input name="course_complete" type="submit" class="course-delete" value="'
1341
                            .  __( 'Delete Course', 'woothemes-sensei' ) . '"/></span>';
1342
1343
                    } // End If Statement
1344
1345
                    $active_html .= '</form>';
1346
1347
                    $active_html .= '</section>';
1348
                }
1349
1350
                $active_html .= '</article>';
1351
			}
1352
1353
			// Active pagination
1354 View Code Duplication
			if( $active_count > $per_page ) {
0 ignored issues
show
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...
1355
1356
				$current_page = 1;
1357
				if( isset( $_GET['active_page'] ) && 0 < intval( $_GET['active_page'] ) ) {
1358
					$current_page = $_GET['active_page'];
1359
				}
1360
1361
				$active_html .= '<nav class="pagination woo-pagination">';
1362
				$total_pages = ceil( $active_count / $per_page );
1363
1364
				if( $current_page > 1 ) {
1365
					$prev_link = add_query_arg( 'active_page', $current_page - 1 );
1366
					$active_html .= '<a class="prev page-numbers" href="' . esc_url( $prev_link ) . '">' . __( 'Previous' , 'woothemes-sensei' ) . '</a> ';
1367
				}
1368
1369
				for ( $i = 1; $i <= $total_pages; $i++ ) {
1370
					$link = add_query_arg( 'active_page', $i );
1371
1372
					if( $i == $current_page ) {
1373
						$active_html .= '<span class="page-numbers current">' . $i . '</span> ';
1374
					} else {
1375
						$active_html .= '<a class="page-numbers" href="' . esc_url( $link ). '">' . $i . '</a> ';
1376
					}
1377
				}
1378
1379
				if( $current_page < $total_pages ) {
1380
					$next_link = add_query_arg( 'active_page', $current_page + 1 );
1381
					$active_html .= '<a class="next page-numbers" href="' . esc_url( $next_link ) . '">' . __( 'Next' , 'woothemes-sensei' ) . '</a> ';
1382
				}
1383
1384
				$active_html .= '</nav>';
1385
			}
1386
1387
			foreach ( $completed_courses as $course_item ) {
1388
				$course = $course_item;
1389
1390
			    // Get Course Categories
1391
			    $category_output = get_the_term_list( $course_item->ID, 'course-category', '', ', ', '' );
1392
1393
		    	$complete_html .= '<article class="' . join( ' ', get_post_class( array( 'course', 'post' ), $course_item->ID ) ) . '">';
1394
1395
		    	    // Image
1396
		    		$complete_html .= Sensei()->course->course_image( absint( $course_item->ID ),100, 100, true );
1397
1398
		    		// Title
1399
		    		$complete_html .= '<header>';
1400
1401
		    		    $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>';
1402
1403
		    		$complete_html .= '</header>';
1404
1405
		    		$complete_html .= '<section class="entry">';
1406
1407
		    			$complete_html .= '<p class="sensei-course-meta">';
1408
1409
		    		    	// Author
1410
		    		    	$user_info = get_userdata( absint( $course_item->post_author ) );
1411 View Code Duplication
		    		    	if ( isset( Sensei()->settings->settings[ 'course_author' ] ) && ( Sensei()->settings->settings[ 'course_author' ] ) ) {
1412
		    		    		$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>';
1413
		    		    	} // End If Statement
1414
1415
		    		    	// Lesson count for this author
1416
		    		    	$complete_html .= '<span class="course-lesson-count">'
1417
                                . Sensei()->course->course_lesson_count( absint( $course_item->ID ) )
1418
                                . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' )
1419
                                . '</span>';
1420
1421
		    		    	// Course Categories
1422
		    		    	if ( '' != $category_output ) {
1423
1424
		    		    		$complete_html .= '<span class="course-category">' . sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ) . '</span>';
1425
1426
		    		    	} // End If Statement
1427
1428
						$complete_html .= '</p>';
1429
1430
						$complete_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>';
1431
1432
                        $complete_html .= $this->get_progress_meter( 100 );
1433
1434
						if( $manage ) {
1435
							$has_quizzes = Sensei()->course->course_quizzes( $course_item->ID, true );
1436
							// Output only if there is content to display
1437
							if ( has_filter( 'sensei_results_links' ) || $has_quizzes ) {
1438
1439
1440
								$complete_html .= '<p class="sensei-results-links">';
1441
								$results_link = '';
1442 View Code Duplication
								if( $has_quizzes ) {
0 ignored issues
show
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...
1443
1444
									$results_link = '<a class="button view-results" href="'
1445
                                        . Sensei()->course_results->get_permalink( $course_item->ID )
1446
                                        . '">' . __( 'View results', 'woothemes-sensei' )
1447
                                        . '</a>';
1448
								}
1449
                                /**
1450
                                 * Filter documented in Sensei_Course::the_course_action_buttons
1451
                                 */
1452
								$complete_html .= apply_filters( 'sensei_results_links', $results_link, $course_item->ID );
1453
								$complete_html .= '</p>';
1454
1455
							}
1456
						}
1457
1458
		    		$complete_html .= '</section>';
1459
1460
		    	$complete_html .= '</article>';
1461
			}
1462
1463
			// Active pagination
1464 View Code Duplication
			if( $completed_count > $per_page ) {
0 ignored issues
show
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...
1465
1466
				$current_page = 1;
1467
				if( isset( $_GET['completed_page'] ) && 0 < intval( $_GET['completed_page'] ) ) {
1468
					$current_page = $_GET['completed_page'];
1469
				}
1470
1471
				$complete_html .= '<nav class="pagination woo-pagination">';
1472
				$total_pages = ceil( $completed_count / $per_page );
1473
1474
1475
				if( $current_page > 1 ) {
1476
					$prev_link = add_query_arg( 'completed_page', $current_page - 1 );
1477
					$complete_html .= '<a class="prev page-numbers" href="' . esc_url( $prev_link ) . '">' . __( 'Previous' , 'woothemes-sensei' ) . '</a> ';
1478
				}
1479
1480
				for ( $i = 1; $i <= $total_pages; $i++ ) {
1481
					$link = add_query_arg( 'completed_page', $i );
1482
1483
					if( $i == $current_page ) {
1484
						$complete_html .= '<span class="page-numbers current">' . $i . '</span> ';
1485
					} else {
1486
						$complete_html .= '<a class="page-numbers" href="' . esc_url( $link ) . '">' . $i . '</a> ';
1487
					}
1488
				}
1489
1490
				if( $current_page < $total_pages ) {
1491
					$next_link = add_query_arg( 'completed_page', $current_page + 1 );
1492
					$complete_html .= '<a class="next page-numbers" href="' . esc_url( $next_link ) . '">' . __( 'Next' , 'woothemes-sensei' ) . '</a> ';
1493
				}
1494
1495
				$complete_html .= '</nav>';
1496
			}
1497
1498
		} // End If Statement
1499
1500
		if( $manage ) {
1501
			$no_active_message = __( 'You have no active courses.', 'woothemes-sensei' );
1502
			$no_complete_message = __( 'You have not completed any courses yet.', 'woothemes-sensei' );
1503
		} else {
1504
			$no_active_message =  __( 'This learner has no active courses.', 'woothemes-sensei' );
1505
			$no_complete_message =  __( 'This learner has not completed any courses yet.', 'woothemes-sensei' );
1506
		}
1507
1508
		ob_start();
1509
		?>
1510
1511
		<?php do_action( 'sensei_before_user_courses' ); ?>
1512
1513
		<?php
1514
		if( $manage && ( ! isset( Sensei()->settings->settings['messages_disable'] ) || ! Sensei()->settings->settings['messages_disable'] ) ) {
1515
			?>
1516
			<p class="my-messages-link-container">
1517
                <a class="my-messages-link" href="<?php echo get_post_type_archive_link( 'sensei_message' ); ?>"
1518
                   title="<?php _e( 'View & reply to private messages sent to your course & lesson teachers.', 'woothemes-sensei' ); ?>">
1519
                    <?php _e( 'My Messages', 'woothemes-sensei' ); ?>
1520
                </a>
1521
            </p>
1522
			<?php
1523
		}
1524
		?>
1525
		<div id="my-courses">
1526
1527
		    <ul>
1528
		    	<li><a href="#active-courses"><?php  _e( 'Active Courses', 'woothemes-sensei' ); ?></a></li>
1529
		    	<li><a href="#completed-courses"><?php  _e( 'Completed Courses', 'woothemes-sensei' ); ?></a></li>
1530
		    </ul>
1531
1532
		    <?php do_action( 'sensei_before_active_user_courses' ); ?>
1533
1534
		    <?php
1535
            $course_page_url = Sensei_Course::get_courses_page_url();
1536
            ?>
1537
1538
		    <div id="active-courses">
1539
1540
		    	<?php if ( '' != $active_html ) {
1541
1542
		    		echo $active_html;
1543
1544
		    	} else { ?>
1545
1546
		    		<div class="sensei-message info">
1547
1548
                        <?php echo $no_active_message; ?>
1549
1550
                        <a href="<?php echo $course_page_url; ?>">
1551
1552
                            <?php  _e( 'Start a Course!', 'woothemes-sensei' ); ?>
1553
1554
                        </a>
1555
1556
                    </div>
1557
1558
		    	<?php } // End If Statement ?>
1559
1560
		    </div>
1561
1562
		    <?php do_action( 'sensei_after_active_user_courses' ); ?>
1563
1564
		    <?php do_action( 'sensei_before_completed_user_courses' ); ?>
1565
1566
		    <div id="completed-courses">
1567
1568
		    	<?php if ( '' != $complete_html ) {
1569
1570
		    		echo $complete_html;
1571
1572
		    	} else { ?>
1573
1574
		    		<div class="sensei-message info">
1575
1576
                        <?php echo $no_complete_message; ?>
1577
1578
                    </div>
1579
1580
		    	<?php } // End If Statement ?>
1581
1582
		    </div>
1583
1584
		    <?php do_action( 'sensei_after_completed_user_courses' ); ?>
1585
1586
		</div>
1587
1588
		<?php do_action( 'sensei_after_user_courses' ); ?>
1589
1590
		<?php
1591
        echo ob_get_clean();
1592
1593
        do_action( 'sensei_after_learner_course_content', $user );
1594
1595
	} // end load_user_courses_content
1596
1597
    /**
1598
     * Returns a list of all courses
1599
     *
1600
     * @since 1.8.0
1601
     * @return array $courses{
1602
     *  @type $course WP_Post
1603
     * }
1604
     */
1605
    public static function get_all_courses(){
1606
1607
        $args = array(
1608
               'post_type' => 'course',
1609
                'posts_per_page' 		=> -1,
1610
                'orderby'         	=> 'title',
1611
                'order'           	=> 'ASC',
1612
                'post_status'      	=> 'any',
1613
                'suppress_filters' 	=> 0,
1614
        );
1615
1616
        $wp_query_obj =  new WP_Query( $args );
1617
1618
        /**
1619
         * sensei_get_all_courses filter
1620
         *
1621
         * This filter runs inside Sensei_Course::get_all_courses.
1622
         *
1623
         * @param array $courses{
1624
         *  @type WP_Post
1625
         * }
1626
         * @param array $attributes
1627
         */
1628
        return apply_filters( 'sensei_get_all_courses' , $wp_query_obj->posts );
1629
1630
    }// end get_all_courses
1631
1632
    /**
1633
     * Generate the course meter component
1634
     *
1635
     * @since 1.8.0
1636
     * @param int $progress_percentage 0 - 100
1637
     * @return string $progress_bar_html
1638
     */
1639
    public function get_progress_meter( $progress_percentage ){
1640
1641
        if ( 50 < $progress_percentage ) {
1642
            $class = ' green';
1643
        } elseif ( 25 <= $progress_percentage && 50 >= $progress_percentage ) {
1644
            $class = ' orange';
1645
        } else {
1646
            $class = ' red';
1647
        }
1648
        $progress_bar_html = '<div class="meter' . esc_attr( $class ) . '"><span style="width: ' . $progress_percentage . '%">' . round( $progress_percentage ) . '%</span></div>';
1649
1650
        return $progress_bar_html;
1651
1652
    }// end get_progress_meter
1653
1654
    /**
1655
     * Generate a statement that tells users
1656
     * how far they are in the course.
1657
     *
1658
     * @param int $course_id
1659
     * @param int $user_id
1660
     *
1661
     * @return string $statement_html
1662
     */
1663
    public function get_progress_statement( $course_id, $user_id ){
1664
1665
        if( empty( $course_id ) || empty( $user_id )
1666
        || ! Sensei_Utils::user_started_course( $course_id, $user_id ) ){
1667
            return '';
1668
        }
1669
1670
        $completed = count( $this->get_completed_lesson_ids( $course_id, $user_id ) );
1671
        $total_lessons = count( $this->course_lessons( $course_id ) );
1672
1673
        $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 );
1674
1675
        /**
1676
         * Filter the course completion statement.
1677
         * Default Currently completed $var lesson($plural) of $var in total
1678
         *
1679
         * @param string $statement
1680
         */
1681
        return apply_filters( 'sensei_course_completion_statement', $statement );
1682
1683
    }// end generate_progress_statement
1684
1685
    /**
1686
     * Output the course progress statement
1687
     *
1688
     * @param $course_id
1689
     * @return void
1690
     */
1691
    public function the_progress_statement( $course_id = 0, $user_id = 0 ){
1692
        if( empty( $course_id ) ){
1693
            global $post;
1694
            $course_id = $post->ID;
1695
        }
1696
1697
        if( empty( $user_id ) ){
1698
            $user_id = get_current_user_id();
1699
        }
1700
1701
        echo '<span class="progress statement  course-completion-rate">' . $this->get_progress_statement( $course_id, $user_id  ) . '</span>';
1702
    }
1703
1704
    /**
1705
     * Output the course progress bar
1706
     *
1707
     * @param $course_id
1708
     * @return void
1709
     */
1710
    public function the_progress_meter( $course_id = 0, $user_id = 0 ){
1711
1712
        if( empty( $course_id ) ){
1713
            global $post;
1714
            $course_id = $post->ID;
1715
        }
1716
1717
        if( empty( $user_id ) ){
1718
            $user_id = get_current_user_id();
1719
        }
1720
1721
        if( 'course' != get_post_type( $course_id ) || ! get_userdata( $user_id )
1722
            || ! Sensei_Utils::user_started_course( $course_id ,$user_id ) ){
1723
            return;
1724
        }
1725
        $percentage_completed = $this->get_completion_percentage( $course_id, $user_id );
1726
1727
        echo $this->get_progress_meter( $percentage_completed );
1728
1729
    }// end the_progress_meter
1730
1731
    /**
1732
     * Checks how many lessons are completed
1733
     *
1734
     * @since 1.8.0
1735
     *
1736
     * @param int $course_id
1737
     * @param int $user_id
1738
     * @return array $completed_lesson_ids
1739
     */
1740
    public function get_completed_lesson_ids( $course_id, $user_id = 0 ){
1741
1742
        if( !( intval( $user_id ) ) > 0 ){
1743
            $user_id = get_current_user_id();
1744
        }
1745
1746
        $completed_lesson_ids = array();
1747
1748
        $course_lessons = $this->course_lessons( $course_id );
1749
1750
        foreach( $course_lessons as $lesson ){
1751
1752
            $is_lesson_completed = Sensei_Utils::user_completed_lesson( $lesson->ID, $user_id );
1753
            if( $is_lesson_completed ){
1754
                $completed_lesson_ids[] = $lesson->ID;
1755
            }
1756
1757
        }
1758
1759
        return $completed_lesson_ids;
1760
1761
    }// end get_completed_lesson_ids
1762
1763
    /**
1764
     * Calculate the perceantage completed in the course
1765
     *
1766
     * @since 1.8.0
1767
     *
1768
     * @param int $course_id
1769
     * @param int $user_id
1770
     * @return int $percentage
1771
     */
1772
    public function get_completion_percentage( $course_id, $user_id = 0 ){
1773
1774
        if( !( intval( $user_id ) ) > 0 ){
1775
            $user_id = get_current_user_id();
1776
        }
1777
1778
        $completed = count( $this->get_completed_lesson_ids( $course_id, $user_id ) );
1779
1780
        if( ! (  $completed  > 0 ) ){
1781
            return 0;
1782
        }
1783
1784
        $total_lessons = count( $this->course_lessons( $course_id ) );
1785
        $percentage = $completed / $total_lessons * 100;
1786
1787
        /**
1788
         *
1789
         * Filter the percentage returned for a users course.
1790
         *
1791
         * @param $percentage
1792
         * @param $course_id
1793
         * @param $user_id
1794
         * @since 1.8.0
1795
         */
1796
        return apply_filters( 'sensei_course_completion_percentage', $percentage, $course_id, $user_id );
1797
1798
    }// end get_completed_lesson_ids
1799
1800
    /**
1801
     * Block email notifications for the specific courses
1802
     * that the user disabled the notifications.
1803
     *
1804
     * @since 1.8.0
1805
     * @param $should_send
1806
     * @return bool
1807
     */
1808
    public function block_notification_emails( $should_send ){
1809
        global $sensei_email_data;
1810
        $email = $sensei_email_data;
1811
1812
        $course_id = '';
1813
1814
        if( isset( $email['course_id'] ) ){
1815
1816
            $course_id = $email['course_id'];
1817
1818
        }elseif( isset( $email['lesson_id'] ) ){
1819
1820
            $course_id = Sensei()->lesson->get_course_id( $email['lesson_id'] );
1821
1822
        }elseif( isset( $email['quiz_id'] ) ){
1823
1824
            $lesson_id = Sensei()->quiz->get_lesson_id( $email['quiz_id'] );
1825
            $course_id = Sensei()->lesson->get_course_id( $lesson_id );
1826
1827
        }
1828
1829
        if( !empty( $course_id ) && 'course'== get_post_type( $course_id ) ) {
1830
1831
            $course_emails_disabled = get_post_meta($course_id, 'disable_notification', true);
1832
1833
            if ($course_emails_disabled) {
1834
1835
                return false;
1836
1837
            }
1838
1839
        }// end if
1840
1841
        return $should_send;
1842
    }// end block_notification_emails
1843
1844
    /**
1845
     * Render the course notification setting meta box
1846
     *
1847
     * @since 1.8.0
1848
     * @param $course
1849
     */
1850
    public function course_notification_meta_box_content( $course ){
1851
1852
        $checked = get_post_meta( $course->ID , 'disable_notification', true );
1853
1854
        // generate checked html
1855
        $checked_html = '';
1856
        if( $checked ){
1857
            $checked_html = 'checked="checked"';
1858
        }
1859
        wp_nonce_field( 'update-course-notification-setting','_sensei_course_notification' );
1860
1861
        echo '<input id="disable_sensei_course_notification" '.$checked_html .' type="checkbox" name="disable_sensei_course_notification" >';
1862
        echo '<label for="disable_sensei_course_notification">'.__('Disable notifications on this course ?', 'woothemes-sensei'). '</label>';
1863
1864
    }// end course_notification_meta_box_content
1865
1866
    /**
1867
     * Store the setting for the course notification setting.
1868
     *
1869
     * @hooked int save_post
1870
     * @since 1.8.0
1871
     *
1872
     * @param $course_id
1873
     */
1874
    public function save_course_notification_meta_box( $course_id ){
1875
1876
        if( !isset( $_POST['_sensei_course_notification']  )
1877
            || ! wp_verify_nonce( $_POST['_sensei_course_notification'], 'update-course-notification-setting' ) ){
1878
            return;
1879
        }
1880
1881
        if( isset( $_POST['disable_sensei_course_notification'] ) && 'on'== $_POST['disable_sensei_course_notification']  ) {
1882
            $new_val = true;
1883
        }else{
1884
            $new_val = false;
1885
        }
1886
1887
       update_post_meta( $course_id , 'disable_notification', $new_val );
1888
1889
    }// end save notification meta box
1890
1891
    /**
1892
     * Backwards compatibility hooks added to ensure that
1893
     * plugins and other parts of sensei still works.
1894
     *
1895
     * This function hooks into `sensei_course_content_inside_before`
1896
     *
1897
     * @since 1.9
1898
     *
1899
     * @param WP_Post $post
1900
     */
1901
    public function content_before_backwards_compatibility_hooks( $post_id ){
1902
1903
        sensei_do_deprecated_action( 'sensei_course_image','1.9.0','sensei_course_content_inside_before' );
1904
        sensei_do_deprecated_action( 'sensei_course_archive_course_title','1.9.0','sensei_course_content_inside_before' );
1905
1906
    }
1907
1908
    /**
1909
     * Backwards compatibility hooks that should be hooked into sensei_loop_course_before
1910
     *
1911
     * hooked into 'sensei_loop_course_before'
1912
     *
1913
     * @since 1.9
1914
     *
1915
     * @global WP_Post $post
1916
     */
1917
    public  function loop_before_backwards_compatibility_hooks( ){
1918
1919
        global $post;
1920
        sensei_do_deprecated_action( 'sensei_course_archive_header','1.9.0','sensei_course_content_inside_before', $post->post_type  );
1921
1922
    }
1923
1924
    /**
1925
     * Output a link to view course. The button text is different depending on the amount of preview lesson available.
1926
     *
1927
     * hooked into 'sensei_course_content_inside_after'
1928
     *
1929
     * @since 1.9.0
1930
     *
1931
     * @param integer $course_id
1932
     */
1933
    public function the_course_free_lesson_preview( $course_id ){
1934
        // Meta data
1935
        $course = get_post( $course_id );
1936
        $preview_lesson_count = intval( Sensei()->course->course_lesson_preview_count( $course->ID ) );
1937
        $is_user_taking_course = Sensei_Utils::user_started_course( $course->ID, get_current_user_id() );
1938
1939 View Code Duplication
        if ( 0 < $preview_lesson_count && !$is_user_taking_course ) {
1940
            ?>
1941
            <p class="sensei-free-lessons">
1942
                <a href="<?php echo get_permalink(); ?>">
1943
                    <?php _e( 'Preview this course', 'woothemes-sensei' ) ?>
1944
                </a>
1945
                - <?php echo sprintf( __( '(%d preview lessons)', 'woothemes-sensei' ), $preview_lesson_count ) ; ?>
1946
            </p>
1947
1948
        <?php
1949
        }
1950
    }
1951
1952
    /**
1953
     * Add course mata to the course meta hook
1954
     *
1955
     * @since 1.9.0
1956
     * @param integer $course_id
1957
     */
1958
    public function the_course_meta( $course_id ){
1959
        echo '<p class="sensei-course-meta">';
1960
1961
        $course = get_post( $course_id );
1962
        $category_output = get_the_term_list( $course->ID, 'course-category', '', ', ', '' );
1963
        $author_display_name = get_the_author_meta( 'display_name', $course->post_author  );
1964
1965
        if ( isset( Sensei()->settings->settings[ 'course_author' ] ) && ( Sensei()->settings->settings[ 'course_author' ] ) ) {?>
1966
1967
            <span class="course-author"><?php _e( 'by ', 'woothemes-sensei' ); ?>
1968
1969
                <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>
1970
1971
            </span>
1972
1973
        <?php } // End If Statement ?>
1974
1975
        <span class="course-lesson-count"><?php echo Sensei()->course->course_lesson_count( $course->ID ) . '&nbsp;' .  __( 'Lessons', 'woothemes-sensei' ); ?></span>
1976
1977
       <?php if ( '' != $category_output ) { ?>
1978
1979
            <span class="course-category"><?php echo sprintf( __( 'in %s', 'woothemes-sensei' ), $category_output ); ?></span>
1980
1981
        <?php } // End If Statement
1982
1983
        // number of completed lessons
1984
        if( Sensei_Utils::user_started_course( $course->ID,  get_current_user_id() )
1985
            || Sensei_Utils::user_completed_course( $course->ID,  get_current_user_id() )  ){
1986
1987
            $completed = count( $this->get_completed_lesson_ids( $course->ID, get_current_user_id() ) );
1988
            $lesson_count = count( $this->course_lessons( $course->ID ) );
1989
            echo '<span class="course-lesson-progress">' . sprintf( __( '%1$d of %2$d lessons completed', 'woothemes-sensei' ) , $completed, $lesson_count  ) . '</span>';
1990
1991
        }
1992
1993
        sensei_simple_course_price( $course->ID );
1994
1995
        echo '</p>';
1996
    } // end the course meta
1997
1998
    /**
1999
     * Filter the classes attached to a post types for courses
2000
     * and add a status class for when the user is logged in.
2001
     *
2002
     * @param $classes
2003
     * @param $class
2004
     * @param $post_id
2005
     *
2006
     * @return array $classes
2007
     */
2008
    public static function add_course_user_status_class( $classes, $class, $course_id ){
2009
2010
        if( 'course' == get_post_type( $course_id )  &&  is_user_logged_in() ){
2011
2012
            if( Sensei_Utils::user_completed_course( $course_id, get_current_user_id() ) ){
2013
2014
                $classes[] = 'user-status-completed';
2015
2016
            }else{
2017
2018
                $classes[] = 'user-status-active';
2019
2020
            }
2021
2022
        }
2023
2024
        return $classes;
2025
2026
    }// end add_course_user_status_class
2027
2028
    /**
2029
     * Prints out the course action buttons links
2030
     *
2031
     * - complete course
2032
     * - delete course
2033
     *
2034
     * @param WP_Post $course
2035
     */
2036
    public static function the_course_action_buttons( $course ){
2037
2038
        if( is_user_logged_in() ) { ?>
2039
2040
            <section class="entry-actions">
2041
                <form method="POST" action="<?php  echo esc_url( remove_query_arg( array( 'active_page', 'completed_page' ) ) ); ?>">
2042
2043
                    <input type="hidden"
2044
                           name="<?php esc_attr_e( 'woothemes_sensei_complete_course_noonce' ) ?>"
2045
                           id="<?php  esc_attr_e( 'woothemes_sensei_complete_course_noonce' ); ?>"
2046
                           value="<?php esc_attr_e( wp_create_nonce( 'woothemes_sensei_complete_course_noonce' ) ); ?>"
2047
                        />
2048
2049
                    <input type="hidden" name="course_complete_id" id="course-complete-id" value="<?php esc_attr_e( intval( $course->ID ) ); ?>" />
2050
2051
                    <?php if ( 0 < absint( count( Sensei()->course->course_lessons( $course->ID ) ) )
2052
                        && Sensei()->settings->settings['course_completion'] == 'complete'
2053
                        && ! Sensei_Utils::user_completed_course( $course, get_current_user_id() )) { ?>
2054
2055
                        <span><input name="course_complete" type="submit" class="course-complete" value="<?php  _e( 'Mark as Complete', 'woothemes-sensei' ); ?>" /></span>
2056
2057
                   <?php  } // End If Statement
2058
2059
                    $course_purchased = false;
2060
                    if ( Sensei_WC::is_woocommerce_active() ) {
2061
                        // Get the product ID
2062
                        $wc_post_id = get_post_meta( intval( $course->ID ), '_course_woocommerce_product', true );
2063
                        if ( 0 < $wc_post_id ) {
2064
2065
                            $user = wp_get_current_user();
2066
                            $course_purchased = Sensei_Utils::sensei_customer_bought_product( $user->user_email, $user->ID, $wc_post_id );
2067
2068
                        } // End If Statement
2069
                    } // End If Statement
2070
2071
                    /**
2072
                     * Hide or show the delete course button.
2073
                     *
2074
                     * This button on shows in certain instances, but this filter will hide it in those
2075
                     * cases. For other instances the button will be hidden.
2076
                     *
2077
                     * @since 1.9.0
2078
                     * @param bool $show_delete_course_button defaults to false
2079
                     */
2080
                    $show_delete_course_button = apply_filters( 'sensei_show_delete_course_button', false );
2081
2082
                    if ( ! $course_purchased
2083
                         && ! Sensei_Utils::user_completed_course( $course->ID, get_current_user_id() )
2084
                         && $show_delete_course_button ) { ?>
2085
2086
                        <span><input name="course_complete" type="submit" class="course-delete" value="<?php echo __( 'Delete Course', 'woothemes-sensei' ); ?>"/></span>
2087
2088
                    <?php } // End If Statement
2089
2090
                    $has_quizzes = Sensei()->course->course_quizzes( $course->ID, true );
2091
                    $results_link = '';
2092 View Code Duplication
                    if( $has_quizzes ){
2093
                        $results_link = '<a class="button view-results" href="' . Sensei()->course_results->get_permalink( $course->ID ) . '">' . __( 'View results', 'woothemes-sensei' ) . '</a>';
2094
                    }
2095
2096
                    // Output only if there is content to display
2097
                    if ( has_filter( 'sensei_results_links' ) || $has_quizzes ) { ?>
2098
2099
                        <p class="sensei-results-links">
2100
                            <?php
2101
                            /**
2102
                             * Filter the results links
2103
                             *
2104
                             * @param string $results_links_html
2105
                             * @param integer $course_id
2106
                             */
2107
                            echo apply_filters( 'sensei_results_links', $results_link, $course->ID );
2108
                            ?>
2109
                        </p>
2110
2111
                    <?php } // end if has filter  ?>
2112
                </form>
2113
            </section>
2114
2115
        <?php  }// end if is user logged in
2116
2117
    }// end the_course_action_buttons
2118
2119
    /**
2120
     * This function alter the main query on the course archive page.
2121
     * This also gives Sensei specific filters that allows variables to be altered specifically on the course archive.
2122
     *
2123
     * This function targets only the course archives and the my courses page. Shortcodes can set their own
2124
     * query parameters via the arguments.
2125
     *
2126
     * This function is hooked into pre_get_posts filter
2127
     *
2128
     * @since 1.9.0
2129
     *
2130
     * @param WP_Query $query
2131
     * @return WP_Query $query
2132
     */
2133
    public static function course_query_filter( $query ){
2134
2135
        // exit early for no course queries and admin queries
2136
        if( is_admin( ) || 'course' != $query->get( 'post_type' ) ){
2137
            return $query;
2138
        }
2139
2140
        global $post; // used to get the current page id for my courses
2141
2142
        // for the course archive page
2143
        if( $query->is_main_query() && is_post_type_archive('course') )
2144
        {
2145
            /**
2146
             * sensei_archive_courses_per_page
2147
             *
2148
             * Sensei courses per page on the course
2149
             * archive
2150
             *
2151
             * @since 1.9.0
2152
             * @param integer $posts_per_page default 10
2153
             */
2154
            $query->set( 'posts_per_page', apply_filters( 'sensei_archive_courses_per_page', 10 ) );
2155
2156
        }
2157
        // for the my courses page
2158
        elseif( is_page() && Sensei()->settings->get( 'my_course_page' ) == $post->ID  )
2159
        {
2160
            /**
2161
             * sensei_my_courses_per_page
2162
             *
2163
             * Sensei courses per page on the my courses page
2164
             * as set in the settings
2165
             *
2166
             * @since 1.9.0
2167
             * @param integer $posts_per_page default 10
2168
             */
2169
            $query->set( 'posts_per_page', apply_filters( 'sensei_my_courses_per_page', 10 ) );
2170
2171
        }
2172
2173
        return $query;
2174
2175
    }// end course_query_filter
2176
2177
    /**
2178
     * Determine the class of the course loop
2179
     *
2180
     * This will output .first or .last and .course-item-number-x
2181
     *
2182
     * @return array $extra_classes
2183
     * @since 1.9.0
2184
     */
2185
    public static function get_course_loop_content_class ()
2186
    {
2187
2188
        global $sensei_course_loop;
2189
2190
2191
        if( !isset( $sensei_course_loop ) ){
2192
            $sensei_course_loop = array();
2193
        }
2194
2195
        if (!isset($sensei_course_loop['counter'])) {
2196
            $sensei_course_loop['counter'] = 0;
2197
        }
2198
2199
        if (!isset($sensei_course_loop['columns'])) {
2200
            $sensei_course_loop['columns'] = self::get_loop_number_of_columns();
2201
        }
2202
2203
        // increment the counter
2204
        $sensei_course_loop['counter']++;
2205
2206
        $extra_classes = array();
2207
        if( 0 == ( $sensei_course_loop['counter'] - 1 ) % $sensei_course_loop['columns'] || 1 == $sensei_course_loop['columns']  ){
2208
            $extra_classes[] = 'first';
2209
        }
2210
2211
        if( 0 == $sensei_course_loop['counter'] % $sensei_course_loop['columns']  ){
2212
            $extra_classes[] = 'last';
2213
        }
2214
2215
        // add the item number to the classes as well.
2216
        $extra_classes[] = 'loop-item-number-'. $sensei_course_loop['counter'];
2217
2218
        /**
2219
         * Filter the course loop class the fires in the  in get_course_loop_content_class function
2220
         * which is called from the course loop content-course.php
2221
         *
2222
         * @since 1.9.0
2223
         *
2224
         * @param array $extra_classes
2225
         * @param WP_Post $loop_current_course
2226
         */
2227
        return apply_filters( 'sensei_course_loop_content_class', $extra_classes ,get_post() );
2228
2229
    }// end get_course_loop_class
2230
2231
    /**
2232
     * Get the number of columns set for Sensei courses
2233
     *
2234
     * @since 1.9.0
2235
     * @return mixed|void
2236
     */
2237
    public static function get_loop_number_of_columns(){
2238
2239
        /**
2240
         * Filter the number of columns on the course archive page.
2241
         *
2242
         * @since 1.9.0
2243
         * @param int $number_of_columns default 1
2244
         */
2245
        return apply_filters('sensei_course_loop_number_of_columns', 1);
2246
2247
    }
2248
2249
    /**
2250
     * Output the course archive filter markup
2251
     *
2252
     * hooked into sensei_loop_course_before
2253
     *
2254
     * @since 1.9.0
2255
     * @param
2256
     */
2257
    public static function course_archive_sorting( $query ){
2258
2259
        // don't show on category pages and other pages
2260
        if( ! is_archive(  'course ') || is_tax('course-category') ){
2261
            return;
2262
        }
2263
2264
        /**
2265
         * Filter the sensei archive course order by values
2266
         *
2267
         * @since 1.9.0
2268
         * @param array $options {
2269
         *  @type string $option_value
2270
         *  @type string $option_string
2271
         * }
2272
         */
2273
        $course_order_by_options = apply_filters( 'sensei_archive_course_order_by_options', array(
2274
            "newness"     => __( "Sort by newest first", "woothemes-sensei"),
2275
            "title"       => __( "Sort by title A-Z", "woothemes-sensei" ),
2276
        ));
2277
2278
        // setup the currently selected item
2279
        $selected = 'newness';
2280
        if( isset( $_GET['orderby'] ) ){
2281
2282
            $selected =  $_GET[ 'orderby' ];
2283
2284
        }
2285
2286
        ?>
2287
2288
        <form class="sensei-ordering" name="sensei-course-order" action="<?php echo esc_attr( Sensei_Utils::get_current_url() ) ; ?>" method="POST">
2289
            <select name="course-orderby" class="orderby">
2290
                <?php
2291
                foreach( $course_order_by_options as $value => $text ){
2292
2293
                    echo '<option value="'. $value . ' "' . selected( $selected, $value, false ) . '>'. $text. '</option>';
2294
2295
                }
2296
                ?>
2297
            </select>
2298
        </form>
2299
2300
    <?php
2301
    }// end course archive filters
2302
2303
    /**
2304
     * Output the course archive filter markup
2305
     *
2306
     * hooked into sensei_loop_course_before
2307
     *
2308
     * @since 1.9.0
2309
     * @param
2310
     */
2311
    public static function course_archive_filters( $query ){
2312
2313
        // don't show on category pages
2314
        if( is_tax('course-category') ){
2315
            return;
2316
        }
2317
2318
        /**
2319
         * filter the course archive filter buttons
2320
         *
2321
         * @since 1.9.0
2322
         * @param array $filters{
2323
         *   @type array ( $id, $url , $title )
2324
         * }
2325
         *
2326
         */
2327
        $filters = apply_filters( 'sensei_archive_course_filter_by_options', array(
2328
            array( 'id' => 'all', 'url' => self::get_courses_page_url(), 'title'=> __( 'All', 'woothemes-sensei' ) ),
2329
            array( 'id' => 'featured', 'url' => add_query_arg( array( 'course_filter'=>'featured'), self::get_courses_page_url()  ), 'title'=> __( 'Featured', 'woothemes-sensei' ) ),
2330
        ));
2331
2332
2333
        ?>
2334
        <ul class="sensei-course-filters clearfix" >
2335
            <?php
2336
2337
            //determine the current active url
2338
            $current_url = Sensei_Utils::get_current_url();
2339
2340
            foreach( $filters as $filter ) {
2341
2342
                $active_class =  $current_url == $filter['url'] ? ' class="active" ' : '';
2343
2344
                echo '<li><a '. $active_class .' id="'. $filter['id'] .'" href="'. esc_url( $filter['url'] ).'" >'. $filter['title']  .'</a></li>';
2345
2346
            }
2347
            ?>
2348
2349
        </ul>
2350
2351
        <?php
2352
2353
    }
2354
2355
    /**
2356
     * if the featured link is clicked on the course archive page
2357
     * filter the courses returned to only show those featured
2358
     *
2359
     * Hooked into pre_get_posts
2360
     *
2361
     * @since 1.9.0
2362
     * @param WP_Query $query
2363
     * @return WP_Query $query
2364
     */
2365
    public static function course_archive_featured_filter( $query ){
2366
2367
        if( isset ( $_GET[ 'course_filter' ] ) && 'featured'== $_GET['course_filter'] && $query->is_main_query()  ){
2368
            //setup meta query for featured courses
2369
            $query->set( 'meta_value', 'featured'  );
2370
            $query->set( 'meta_key', '_course_featured'  );
2371
            $query->set( 'meta_compare', '='  );
2372
        }
2373
2374
        return $query;
2375
    }
2376
2377
    /**
2378
     * if the course order drop down is changed
2379
     *
2380
     * Hooked into pre_get_posts
2381
     *
2382
     * @since 1.9.0
2383
     * @param WP_Query $query
2384
     * @return WP_Query $query
2385
     */
2386
    public static function course_archive_order_by_title( $query ){
2387
2388
        if( isset ( $_POST[ 'course-orderby' ] ) && 'title '== $_POST['course-orderby']
2389
            && 'course'== $query->get('post_type') && $query->is_main_query()  ){
2390
            // setup the order by title for this query
2391
            $query->set( 'orderby', 'title'  );
2392
            $query->set( 'order', 'ASC'  );
2393
        }
2394
2395
        return $query;
2396
    }
2397
2398
2399
    /**
2400
     * Get the link to the courses page. This will be the course post type archive
2401
     * page link or the page the user set in their settings
2402
     *
2403
     * @since 1.9.0
2404
     * @return string $course_page_url
2405
     */
2406
    public static function get_courses_page_url(){
2407
2408
        $course_page_id = intval( Sensei()->settings->settings[ 'course_page' ] );
2409
        $course_page_url = empty( $course_page_id ) ? get_post_type_archive_link('course') : get_permalink( $course_page_id );
2410
2411
        return $course_page_url;
2412
2413
    }// get_course_url
2414
2415
    /**
2416
     * Output the headers on the course archive page
2417
     *
2418
     * Hooked into the sensei_archive_title
2419
     *
2420
     * @since 1.9.0
2421
     * @param string $query_type
2422
     * @param string $before_html
2423
     * @param string $after_html
2424
     * @return void
2425
     */
2426
    public static function archive_header( $query_type ='' , $before_html='', $after_html =''  ){
2427
2428
        if( ! is_post_type_archive('course') ){
2429
            return;
2430
        }
2431
2432
        // deprecated since 1.9.0
2433
        sensei_do_deprecated_action('sensei_archive_title','1.9.0','sensei_archive_before_course_loop');
2434
2435
        $html = '';
2436
2437
        if( empty( $before_html ) ){
2438
2439
            $before_html = '<header class="archive-header"><h1>';
2440
2441
        }
2442
2443
        if( empty( $after_html ) ){
2444
2445
            $after_html = '</h1></header>';
2446
2447
        }
2448
2449
        if ( is_tax( 'course-category' ) ) {
2450
2451
            global $wp_query;
2452
2453
            $taxonomy_obj = $wp_query->get_queried_object();
2454
            $taxonomy_short_name = $taxonomy_obj->taxonomy;
2455
            $taxonomy_raw_obj = get_taxonomy( $taxonomy_short_name );
2456
            $title = sprintf( __( '%1$s Archives: %2$s', 'woothemes-sensei' ), $taxonomy_raw_obj->labels->name, $taxonomy_obj->name );
2457
            echo apply_filters( 'course_category_archive_title', $before_html . $title . $after_html );
2458
            return;
2459
2460
        } // End If Statement
2461
2462
        switch ( $query_type ) {
2463
            case 'newcourses':
2464
                $html .= $before_html . __( 'New Courses', 'woothemes-sensei' ) . $after_html;
2465
                break;
2466
            case 'featuredcourses':
2467
                $html .= $before_html .  __( 'Featured Courses', 'woothemes-sensei' ) . $after_html;
2468
                break;
2469
            case 'freecourses':
2470
                $html .= $before_html .  __( 'Free Courses', 'woothemes-sensei' ) . $after_html;
2471
                break;
2472
            case 'paidcourses':
2473
                $html .= $before_html .  __( 'Paid Courses', 'woothemes-sensei' ) . $after_html;
2474
                break;
2475
            default:
2476
                $html .= $before_html . __( 'Courses', 'woothemes-sensei' ) . $after_html;
2477
                break;
2478
        } // End Switch Statement
2479
2480
        echo apply_filters( 'course_archive_title', $html );
2481
2482
    }//course_archive_header
2483
2484
2485
    /**
2486
     * Filter the single course content
2487
     * taking into account if the user has access.
2488
     *
2489
     * @1.9.0
2490
     *
2491
     * @param string $content
2492
     * @return string $content or $excerpt
2493
     */
2494
    public static function single_course_content( $content ){
2495
2496
        if( ! is_singular('course') ){
2497
2498
            return $content;
2499
2500
        }
2501
2502
        // Content Access Permissions
2503
        $access_permission = false;
2504
2505
        if ( ! Sensei()->settings->get('access_permission')  || sensei_all_access() ) {
2506
2507
            $access_permission = true;
2508
2509
        } // End If Statement
2510
2511
        // Check if the user is taking the course
2512
        $is_user_taking_course = Sensei_Utils::user_started_course( get_the_ID(), get_current_user_id() );
2513
2514
        if(Sensei_WC::is_woocommerce_active()) {
2515
2516
            $wc_post_id = get_post_meta( get_the_ID(), '_course_woocommerce_product', true );
2517
            $product = Sensei()->sensei_get_woocommerce_product_object( $wc_post_id );
2518
2519
            $has_product_attached = isset ( $product ) && is_object ( $product );
2520
2521
        } else {
2522
2523
            $has_product_attached = false;
2524
2525
        }
2526
2527
        if ( ( is_user_logged_in() && $is_user_taking_course )
2528
            || ( $access_permission && !$has_product_attached)
2529
            || 'full' == Sensei()->settings->get( 'course_single_content_display' ) ) {
2530
2531
	        // compensate for core providing and empty $content
2532
2533
	        if( empty( $content ) ){
2534
		        remove_filter( 'the_content', array( 'Sensei_Course', 'single_course_content') );
2535
		        $course = get_post( get_the_ID() );
2536
2537
		        $content = apply_filters( 'the_content', $course->post_content );
2538
2539
	        }
2540
2541
            return $content;
2542
2543
        } else {
2544
2545
            return '<p class="course-excerpt">' . get_post(  get_the_ID() )->post_excerpt . '</p>';
2546
2547
        }
2548
2549
    }// end single_course_content
2550
2551
    /**
2552
     * Output the the single course lessons title with markup.
2553
     *
2554
     * @since 1.9.0
2555
     */
2556
    public static function the_course_lessons_title(){
2557
2558
	    if ( ! is_singular( 'course' )  ) {
2559
		    return;
2560
	    }
2561
2562
        global $post;
2563
        $none_module_lessons = Sensei()->modules->get_none_module_lessons( $post->ID  );
2564
        $course_lessons = Sensei()->course->course_lessons( $post->ID );
2565
2566
        // title should be Other Lessons if there are lessons belonging to models.
2567
        $title = __('Other Lessons', 'woothemes-sensei');
2568
2569
        // show lessons if the number of lesson in the course is the same as those that isn't assigned to a module
2570
        if( count( $course_lessons ) == count( $none_module_lessons )  ){
2571
2572
            $title = __('Lessons', 'woothemes-sensei');
2573
2574
        }elseif( empty( $none_module_lessons ) ){ // if the none module lessons are simply empty the title should not be shown
2575
2576
            $title = '';
2577
        }
2578
2579
        /**
2580
         * hook document in class-woothemes-sensei-message.php
2581
         */
2582
        $title = apply_filters( 'sensei_single_title', $title, $post->post_type );
2583
2584
        ob_start(); // start capturing the following output.
2585
2586
        ?>
2587
2588
            <header>
2589
                <h2> <?php echo $title; ?> </h2>
2590
            </header>
2591
2592
        <?php
2593
2594
        /**
2595
         * Filter the title and markup that appears above the lessons on a single course
2596
         * page.
2597
         *
2598
         * @since 1.9.0
2599
         * @param string $lessons_title_html
2600
         */
2601
        echo apply_filters('the_course_lessons_title', ob_get_clean() ); // output and filter the captured output and stop capturing.
2602
2603
    }// end the_course_lessons_title
2604
2605
    /**
2606
     * This function loads the global wp_query object with with lessons
2607
     * of the current course. It is designed to be used on the single-course template
2608
     * and expects the global post to be a singular course.
2609
     *
2610
     * This function excludes lessons belonging to modules as they are
2611
     * queried separately.
2612
     *
2613
     * @since 1.9.0
2614
     * @global $wp_query
2615
     */
2616
    public static function load_single_course_lessons_query(){
2617
2618
        global $post, $wp_query;
2619
2620
        $course_id = $post->ID;
2621
2622
        if( 'course' != get_post_type( $course_id ) ){
2623
            return;
2624
        }
2625
2626
        $course_lesson_query_args = array(
2627
            'post_type'         => 'lesson',
2628
            'posts_per_page'    => 500,
2629
            'orderby'           => 'date',
2630
            'order'             => 'ASC',
2631
            'meta_query'        => array(
2632
                array(
2633
                    'key' => '_lesson_course',
2634
                    'value' => intval( $course_id ),
2635
                ),
2636
            ),
2637
            'post_status'       => 'public',
2638
            'suppress_filters'  => 0,
2639
        );
2640
2641
        // Exclude lessons belonging to modules as they are queried along with the modules.
2642
        $modules = Sensei()->modules->get_course_modules( $course_id );
2643
        if( !is_wp_error( $modules ) && ! empty( $modules ) && is_array( $modules ) ){
2644
2645
            $terms_ids = array();
2646
            foreach( $modules as $term ){
2647
2648
                $terms_ids[] = $term->term_id;
2649
2650
            }
2651
2652
            $course_lesson_query_args[ 'tax_query'] = array(
2653
                array(
2654
                    'taxonomy' => 'module',
2655
                    'field'    => 'id',
2656
                    'terms'    => $terms_ids,
2657
                    'operator' => 'NOT IN',
2658
                ),
2659
            );
2660
        }
2661
2662
        //setting lesson order
2663
        $course_lesson_order = get_post_meta( $course_id, '_lesson_order', true);
2664
        if( !empty( $course_lesson_order ) ){
2665
2666
            $course_lesson_query_args['post__in'] = explode( ',', $course_lesson_order );
2667
            $course_lesson_query_args['orderby']= 'post__in' ;
2668
            unset( $course_lesson_query_args['order'] );
2669
2670
        }
2671
2672
        $wp_query = new WP_Query( $course_lesson_query_args );
2673
2674
    }// load_single_course_lessons
2675
2676
    /**
2677
     * Flush the rewrite rules for a course post type
2678
     *
2679
     * @since 1.9.0
2680
     *
2681
     * @param $post_id
2682
     */
2683 View Code Duplication
    public static function flush_rewrite_rules( $post_id ){
2684
2685
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
2686
2687
            return;
2688
2689
        }
2690
2691
2692
        if( 'course' == get_post_type( $post_id )  ){
2693
2694
            Sensei()->initiate_rewrite_rules_flush();
2695
2696
        }
2697
2698
    }
2699
2700
    /**
2701
     * Optionally return the full content on the single course pages
2702
     * depending on the users course_single_content_display setting
2703
     *
2704
     * @since 1.9.0
2705
     * @param $excerpt
2706
     * @return string
2707
     */
2708
    public static function full_content_excerpt_override( $excerpt ){
2709
2710
        if (   is_singular('course')  &&
2711
                'full' == Sensei()->settings->get( 'course_single_content_display' ) ){
2712
2713
            return get_the_content();
2714
2715
        } else {
2716
2717
            return $excerpt;
2718
2719
        }
2720
2721
    }
2722
2723
    /**
2724
     * Output the course actions like start taking course, register, add to cart etc.
2725
     *
2726
     * @since 1.9.0
2727
     */
2728
    public static function the_course_enrolment_actions(){
2729
2730
	    global $post;
2731
2732
	    if ( 'course' != $post->post_type ) {
2733
			return;
2734
	    }
2735
2736
        ?>
2737
        <section class="course-meta course-enrolment">
2738
        <?php
2739
        global  $post, $current_user;
2740
        $is_user_taking_course = Sensei_Utils::user_started_course( $post->ID, $current_user->ID );
2741
2742
	    if ( is_user_logged_in() && ! $is_user_taking_course ) {
2743
2744
	        // Check for woocommerce
2745
	        if ( Sensei_WC::is_woocommerce_active() && Sensei_WC::is_course_purchasable( $post->ID ) ) {
2746
2747
		        // Get the product ID
2748
                Sensei_WC::the_add_to_cart_button_html($post->ID );
2749
2750
            } else {
2751
2752
                sensei_start_course_form($post->ID);
2753
2754
            } // End If Statement
2755
2756
        } elseif ( is_user_logged_in() ) {
2757
2758
            // Check if course is completed
2759
            $user_course_status = Sensei_Utils::user_course_status( $post->ID, $current_user->ID );
2760
            $completed_course = Sensei_Utils::user_completed_course( $user_course_status );
2761
            // Success message
2762
            if ( $completed_course ) { ?>
2763
                <div class="status completed"><?php  _e( 'Completed', 'woothemes-sensei' ); ?></div>
2764
                <?php
2765
                $has_quizzes = Sensei()->course->course_quizzes( $post->ID, true );
2766
                if( has_filter( 'sensei_results_links' ) || $has_quizzes ) { ?>
2767
                    <p class="sensei-results-links">
2768
                        <?php
2769
                        $results_link = '';
2770 View Code Duplication
                        if( $has_quizzes ) {
2771
                            $results_link = '<a class="view-results" href="' . Sensei()->course_results->get_permalink( $post->ID ) . '">' .  __( 'View results', 'woothemes-sensei' ) . '</a>';
2772
                        }
2773
                        /**
2774
                         * Filter documented in Sensei_Course::the_course_action_buttons
2775
                         */
2776
                        $results_link = apply_filters( 'sensei_results_links', $results_link, $post->ID );
2777
                        echo $results_link;
2778
                        ?></p>
2779
                <?php } ?>
2780
            <?php } else { ?>
2781
                <div class="status in-progress"><?php echo __( 'In Progress', 'woothemes-sensei' ); ?></div>
2782
            <?php }
2783
2784
        } else {
2785
2786
            // Check for woocommerce
2787
		    if ( Sensei_WC::is_woocommerce_active() && Sensei_WC::is_course_purchasable( $post->ID ) ) {
2788
2789
	            $login_link =  '<a href="' . sensei_user_login_url() . '">' . __( 'log in', 'woothemes-sensei' ) . '</a>';
2790
	            $message = sprintf( __( 'Or %1$s to access your purchased courses', 'woothemes-sensei' ), $login_link );
2791
	            Sensei()->notices->add_notice( $message, 'info' ) ;
2792
	            Sensei_WC::the_add_to_cart_button_html( $post->ID );
2793
2794
            } else {
2795
2796
                if( get_option( 'users_can_register') ) {
2797
2798
	                // set the permissions message
2799
	                $anchor_before = '<a href="' . esc_url( sensei_user_login_url() ) . '" >';
2800
	                $anchor_after = '</a>';
2801
	                $notice = sprintf(
2802
		                __('or log in to view this courses. Click here to %slogin%s.'),
2803
		                $anchor_before,
2804
		                $anchor_after
2805
	                );
2806
2807
	                // register the notice to display
2808
	                if( Sensei()->settings->get( 'access_permission' ) ){
2809
		                Sensei()->notices->add_notice( $notice, 'info' ) ;
2810
	                }
2811
2812
2813
                    $my_courses_page_id = '';
2814
2815
                    /**
2816
                     * Filter to force Sensei to output the default WordPress user
2817
                     * registration link.
2818
                     *
2819
                     * @since 1.9.0
2820
                     * @param bool $wp_register_link default false
2821
                     */
2822
2823
                    $wp_register_link = apply_filters('sensei_use_wp_register_link', false);
2824
2825
                    $settings = Sensei()->settings->get_settings();
2826 View Code Duplication
                    if( isset( $settings[ 'my_course_page' ] )
2827
                        && 0 < intval( $settings[ 'my_course_page' ] ) ){
2828
2829
                        $my_courses_page_id = $settings[ 'my_course_page' ];
2830
2831
                    }
2832
2833
                    // If a My Courses page was set in Settings, and 'sensei_use_wp_register_link'
2834
                    // is false, link to My Courses. If not, link to default WordPress registration page.
2835 View Code Duplication
                    if( !empty( $my_courses_page_id ) && $my_courses_page_id && !$wp_register_link){
2836
2837
                        $my_courses_url = get_permalink( $my_courses_page_id  );
2838
                        $register_link = '<a href="'.$my_courses_url. '">' . __('Register', 'woothemes-sensei') .'</a>';
2839
                        echo '<div class="status register">' . $register_link . '</div>' ;
2840
2841
                    } else{
2842
2843
                        wp_register( '<div class="status register">', '</div>' );
2844
2845
                    }
2846
2847
                } // end if user can register
2848
2849
            } // End If Statement
2850
2851
        } // End If Statement ?>
2852
2853
        </section><?php
2854
2855
    }// end the_course_enrolment_actions
2856
2857
    /**
2858
     * Output the course video inside the loop.
2859
     *
2860
     * @since 1.9.0
2861
     */
2862 View Code Duplication
    public static function the_course_video(){
2863
2864
        global $post;
2865
2866
	    if ( ! is_singular( 'course' )  ) {
2867
		    return;
2868
	    }
2869
        // Get the meta info
2870
        $course_video_embed = get_post_meta( $post->ID, '_course_video_embed', true );
2871
2872
        if ( 'http' == substr( $course_video_embed, 0, 4) ) {
2873
2874
            $course_video_embed = wp_oembed_get( esc_url( $course_video_embed ) );
2875
2876
        } // End If Statement
2877
2878
        if ( '' != $course_video_embed ) { ?>
2879
2880
            <div class="course-video">
2881
                <?php echo html_entity_decode($course_video_embed); ?>
2882
            </div>
2883
2884
        <?php } // End If Statement
2885
    }
2886
2887
    /**
2888
     * Output the title for the single lesson page
2889
     *
2890
     * @global $post
2891
     * @since 1.9.0
2892
     */
2893
    public static function the_title(){
2894
2895
	    if( ! is_singular( 'course' ) ){
2896
			return;
2897
	    }
2898
        global $post;
2899
2900
        ?>
2901
        <header>
2902
2903
            <h1>
2904
2905
                <?php
2906
                /**
2907
                 * Filter documented in class-sensei-messages.php the_title
2908
                 */
2909
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
2910
                ?>
2911
2912
            </h1>
2913
2914
        </header>
2915
2916
        <?php
2917
2918
    }//the_title
2919
2920
    /**
2921
     * Show the title on the course category pages
2922
     *
2923
     * @since 1.9.0
2924
     */
2925
    public static function course_category_title(){
2926
2927
        if( ! is_tax( 'course-category' ) ){
2928
            return;
2929
        }
2930
2931
        $category_slug = get_query_var('course-category');
2932
        $term  = get_term_by('slug',$category_slug,'course-category');
2933
2934
        if( ! empty($term) ){
2935
2936
            $title = $term->name;
2937
2938
        }else{
2939
2940
            $title = 'Course Category';
2941
2942
        }
2943
2944
        $html = '<h2 class="sensei-category-title">';
2945
        $html .= __('Category') . ' ' . $title;
2946
        $html .= '</h2>';
2947
2948
        echo apply_filters( 'course_category_title', $html , $term->term_id );
2949
2950
    }// course_category_title
2951
2952
    /**
2953
     * Alter the course query to respect the order set for courses and apply
2954
     * this on the course-category pages.
2955
     *
2956
     * @since 1.9.0
2957
     *
2958
     * @param WP_Query $query
2959
     * @return WP_Query
2960
     */
2961
    public static function alter_course_category_order( $query ){
2962
2963
        if( ! is_tax( 'course-category' ) || ! $query->is_main_query() ){
2964
            return $query;
2965
        }
2966
2967
        $order = get_option( 'sensei_course_order', '' );
2968
        if( !empty( $order )  ){
2969
            $query->set('orderby', 'menu_order' );
2970
            $query->set('order', 'ASC' );
2971
        }
2972
2973
        return $query;
2974
2975
    }
2976
2977
    /**
2978
     * The very basic course query arguments
2979
     * so we don't have to repeat this througout
2980
     * the code base.
2981
     *
2982
     * Usage:
2983
     * $args = Sensei_Course::get_default_query_args();
2984
     * $args['custom_arg'] ='custom value';
2985
     * $courses = get_posts( $args )
2986
     *
2987
     * @since 1.9.0
2988
     *
2989
     * @return array
2990
     */
2991
    public static function get_default_query_args(){
2992
        return array(
2993
            'post_type' 		=> 'course',
2994
            'posts_per_page' 		=> 1000,
2995
            'orderby'         	=> 'date',
2996
            'order'           	=> 'DESC',
2997
            'suppress_filters' 	=> 0
2998
        );
2999
    }
3000
3001
    /**
3002
     * Check if the prerequisite course is completed
3003
     * Courses with no pre-requisite should always return true
3004
     *
3005
     * @since 1.9.0
3006
     * @param $course_id
3007
     * @return bool
3008
     */
3009
    public static function is_prerequisite_complete( $course_id ){
3010
3011
        $course_prerequisite_id = get_post_meta( $course_id, '_course_prerequisite', true );
3012
3013
        // if it has a pre requisite course check it
3014
        if( ! empty(  $course_prerequisite_id ) ){
3015
3016
            return Sensei_Utils::user_completed_course( $course_prerequisite_id, get_current_user_id() );
3017
3018
        }
3019
3020
        return true;
3021
3022
    }// end is_prerequisite_complete
3023
3024
3025
}// End Class
3026
3027
/**
3028
 * Class WooThemes_Sensei_Course
3029
 * @ignore only for backward compatibility
3030
 * @since 1.9.0
3031
 */
3032
class WooThemes_Sensei_Course extends Sensei_Course{}
3033