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-lesson.php (23 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 14 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 Lessons Class
6
 *
7
 * All functionality pertaining to the lessons post type in Sensei.
8
 *
9
 * @package Content
10
 * @author Automattic
11
 *
12
 * @since 1.0.0
13
 */
14
class Sensei_Lesson {
15
	public $token;
16
	public $meta_fields;
17
18
	/**
19
	 * Constructor.
20
	 * @since  1.0.0
21
	 */
22
	public function __construct () {
23
24
        $this->token = 'lesson';
25
26
		// Setup meta fields for this post type
27
		$this->meta_fields = array( 'lesson_prerequisite', 'lesson_course', 'lesson_preview', 'lesson_length', 'lesson_complexity', 'lesson_video_embed' );
28
29
        $this->question_order = '';
30
31
		// Admin actions
32
		if ( is_admin() ) {
33
34
			// Metabox functions
35
			add_action( 'admin_menu', array( $this, 'meta_box_setup' ), 20 );
36
			add_action( 'save_post', array( $this, 'meta_box_save' ) );
37
			add_action( 'save_post', array( $this, 'quiz_update' ) );
38
39
			// Custom Write Panel Columns
40
			add_filter( 'manage_edit-lesson_columns', array( $this, 'add_column_headings' ), 10, 1 );
41
			add_action( 'manage_posts_custom_column', array( $this, 'add_column_data' ), 10, 2 );
42
43
			// Add/Update question
44
			add_action( 'wp_ajax_lesson_update_question', array( $this, 'lesson_update_question' ) );
45
			add_action( 'wp_ajax_nopriv_lesson_update_question', array( $this, 'lesson_update_question' ) );
46
47
			// Add course
48
			add_action( 'wp_ajax_lesson_add_course', array( $this, 'lesson_add_course' ) );
49
			add_action( 'wp_ajax_nopriv_lesson_add_course', array( $this, 'lesson_add_course' ) );
50
51
			// Update grade type
52
			add_action( 'wp_ajax_lesson_update_grade_type', array( $this, 'lesson_update_grade_type' ) );
53
			add_action( 'wp_ajax_nopriv_lesson_update_grade_type', array( $this, 'lesson_update_grade_type' ) );
54
55
			// Update question order
56
			add_action( 'wp_ajax_lesson_update_question_order', array( $this, 'lesson_update_question_order' ) );
57
			add_action( 'wp_ajax_nopriv_lesson_update_question_order', array( $this, 'lesson_update_question_order' ) );
58
59
			//Update question order
60
			add_action( 'wp_ajax_lesson_update_question_order_random', array( $this, 'lesson_update_question_order_random' ) );
61
			add_action( 'wp_ajax_nopriv_lesson_update_question_order_random', array( $this, 'lesson_update_question_order_random' ) );
62
63
			// Get answer ID
64
			add_action( 'wp_ajax_question_get_answer_id', array( $this, 'question_get_answer_id' ) );
65
			add_action( 'wp_ajax_nopriv_question_get_answer_id', array( $this, 'question_get_answer_id' ) );
66
67
			// Add multiple questions
68
			add_action( 'wp_ajax_lesson_add_multiple_questions', array( $this, 'lesson_add_multiple_questions' ) );
69
			add_action( 'wp_ajax_nopriv_lesson_add_multiple_questions', array( $this, 'lesson_add_multiple_questions' ) );
70
71
			// Remove multiple questions
72
			add_action( 'wp_ajax_lesson_remove_multiple_questions', array( $this, 'lesson_remove_multiple_questions' ) );
73
			add_action( 'wp_ajax_nopriv_lesson_remove_multiple_questions', array( $this, 'lesson_remove_multiple_questions' ) );
74
75
			// Get question category limit
76
			add_action( 'wp_ajax_get_question_category_limit', array( $this, 'get_question_category_limit' ) );
77
			add_action( 'wp_ajax_nopriv_get_question_category_limit', array( $this, 'get_question_category_limit' ) );
78
79
			// Add existing questions
80
			add_action( 'wp_ajax_lesson_add_existing_questions', array( $this, 'lesson_add_existing_questions' ) );
81
			add_action( 'wp_ajax_nopriv_lesson_add_existing_questions', array( $this, 'lesson_add_existing_questions' ) );
82
83
			// Filter existing questions
84
			add_action( 'wp_ajax_filter_existing_questions', array( $this, 'quiz_panel_filter_existing_questions' ) );
85
			add_action( 'wp_ajax_nopriv_filter_existing_questions', array( $this, 'quiz_panel_filter_existing_questions' ) );
86
87
            // output bulk edit fields
88
            add_action( 'bulk_edit_custom_box', array( $this, 'all_lessons_edit_fields' ), 10, 2 );
89
            add_action( 'quick_edit_custom_box', array( $this, 'all_lessons_edit_fields' ), 10, 2 );
90
91
            // load quick edit default values
92
            add_action('manage_lesson_posts_custom_column', array( $this, 'set_quick_edit_admin_defaults'), 11, 2);
93
94
            // save bulk edit fields
95
            add_action( 'wp_ajax_save_bulk_edit_book', array( $this, 'save_all_lessons_edit_fields' ) );
96
97
            // flush rewrite rules when saving a lesson
98
            add_action('save_post', array( __CLASS__, 'flush_rewrite_rules' ) );
99
100
		} else {
0 ignored issues
show
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
101
			// Frontend actions
102
		} // End If Statement
103
	} // End __construct()
104
105
	/**
106
	 * meta_box_setup function.
107
	 *
108
	 * @access public
109
	 * @return void
110
	 */
111
	public function meta_box_setup () {
112
113
		// Add Meta Box for Prerequisite Lesson
114
		add_meta_box( 'lesson-prerequisite', __( 'Lesson Prerequisite', 'woothemes-sensei' ), array( $this, 'lesson_prerequisite_meta_box_content' ), $this->token, 'side', 'default' );
115
116
		// Add Meta Box for Lesson Course
117
		add_meta_box( 'lesson-course', __( 'Lesson Course', 'woothemes-sensei' ), array( $this, 'lesson_course_meta_box_content' ), $this->token, 'side', 'default' );
118
119
		// Add Meta Box for Lesson Preview
120
		add_meta_box( 'lesson-preview', __( 'Lesson Preview', 'woothemes-sensei' ), array( $this, 'lesson_preview_meta_box_content' ), $this->token, 'side', 'default' );
121
122
		// Add Meta Box for Lesson Information
123
		add_meta_box( 'lesson-info', __( 'Lesson Information', 'woothemes-sensei' ), array( $this, 'lesson_info_meta_box_content' ), $this->token, 'normal', 'default' );
124
125
		// Add Meta Box for Quiz Settings
126
		add_meta_box( 'lesson-quiz-settings', __( 'Quiz Settings', 'woothemes-sensei' ), array( $this, 'lesson_quiz_settings_meta_box_content' ), $this->token, 'normal', 'default' );
127
128
		// Add Meta Box for Lesson Quiz Questions
129
		add_meta_box( 'lesson-quiz', __( 'Quiz Questions', 'woothemes-sensei' ), array( $this, 'lesson_quiz_meta_box_content' ), $this->token, 'normal', 'default' );
130
131
		// Remove "Custom Settings" meta box.
132
		remove_meta_box( 'woothemes-settings', $this->token, 'normal' );
133
134
		// Add JS scripts
135
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
136
137
		// Add CSS
138
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
139
140
	} // End meta_box_setup()
141
142
143
	/**
144
	 * lesson_info_meta_box_content function.
145
	 *
146
	 * @access public
147
	 * @return void
148
	 */
149
	public function lesson_info_meta_box_content () {
150
		global $post;
151
152
		$lesson_length = get_post_meta( $post->ID, '_lesson_length', true );
153
		$lesson_complexity = get_post_meta( $post->ID, '_lesson_complexity', true );
154
		$complexity_array = $this->lesson_complexities();
155
		$lesson_video_embed = get_post_meta( $post->ID, '_lesson_video_embed', true );
156
157
		$html = '';
158
		// Lesson Length
159
		$html .= '<p><label for="lesson_length">' . __( 'Lesson Length in minutes', 'woothemes-sensei' ) . ': </label>';
160
		$html .= '<input type="number" id="lesson-length" name="lesson_length" class="small-text" value="' . esc_attr( $lesson_length ) . '" /></p>' . "\n";
161
		// Lesson Complexity
162
		$html .= '<p><label for="lesson_complexity">' . __( 'Lesson Complexity', 'woothemes-sensei' ) . ': </label>';
163
		$html .= '<select id="lesson-complexity-options" name="lesson_complexity" class="chosen_select lesson-complexity-select">';
164
			$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
165
			foreach ($complexity_array as $key => $value){
166
				$html .= '<option value="' . esc_attr( $key ) . '"' . selected( $key, $lesson_complexity, false ) . '>' . esc_html( $value ) . '</option>' . "\n";
167
			} // End For Loop
168
		$html .= '</select></p>' . "\n";
169
170
		$html .= '<p><label for="lesson_video_embed">' . __( 'Video Embed Code', 'woothemes-sensei' ) . ':</label><br/>' . "\n";
171
		$html .= '<textarea rows="5" cols="50" name="lesson_video_embed" tabindex="6" id="course-video-embed">' . $lesson_video_embed . '</textarea></p>' . "\n";
172
		$html .= '<p>' .  __( 'Paste the embed code for your video (e.g. YouTube, Vimeo etc.) in the box above.', 'woothemes-sensei' ) . '</p>';
173
174
		echo $html;
175
176
	} // End lesson_info_meta_box_content()
177
178
	/**
179
	 * lesson_prerequisite_meta_box_content function.
180
	 *
181
	 * @access public
182
	 * @return void
183
	 */
184
	public function lesson_prerequisite_meta_box_content () {
185
		global $post;
186
		// Get existing post meta
187
		$select_lesson_prerequisite = get_post_meta( $post->ID, '_lesson_prerequisite', true );
188
		// Get the Lesson Posts
189
		$post_args = array(	'post_type' 		=> 'lesson',
190
							'posts_per_page' 		=> -1,
191
							'orderby'         	=> 'title',
192
    						'order'           	=> 'ASC',
193
    						'exclude' 			=> $post->ID,
194
							'suppress_filters' 	=> 0
195
							);
196
		$posts_array = get_posts( $post_args );
197
		// Build the HTML to Output
198
		$html = '';
199
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
200 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...
201
			$html .= '<select id="lesson-prerequisite-options" name="lesson_prerequisite" class="chosen_select widefat">' . "\n";
202
			$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
203
				foreach ($posts_array as $post_item){
204
					$html .= '<option value="' . esc_attr( absint( $post_item->ID ) ) . '"' . selected( $post_item->ID, $select_lesson_prerequisite, false ) . '>' . esc_html( $post_item->post_title ) . '</option>' . "\n";
205
				} // End For Loop
206
			$html .= '</select>' . "\n";
207
		} else {
208
			$html .= '<p>' . esc_html( __( 'No lessons exist yet. Please add some first.', 'woothemes-sensei' ) ) . '</p>';
209
		} // End If Statement
210
		// Output the HTML
211
		echo $html;
212
	} // End lesson_prerequisite_meta_box_content()
213
214
	/**
215
	 * lesson_preview_meta_box_content function.
216
	 *
217
	 * @access public
218
	 * @return void
219
	 */
220
	public function lesson_preview_meta_box_content () {
221
		global $post;
222
		// Get existing post meta
223
		$lesson_preview = get_post_meta( $post->ID, '_lesson_preview', true );
224
		$html = '';
225
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
226
227
		$checked = '';
228
		if ( isset( $lesson_preview ) && ( '' != $lesson_preview ) ) {
229
	 	    $checked = checked( 'preview', $lesson_preview, false );
230
	 	} // End If Statement
231
232
	 	$html .= '<label for="lesson_preview">';
233
	 	$html .= '<input type="checkbox" id="lesson_preview" name="lesson_preview" value="preview" ' . $checked . '>&nbsp;' . __( 'Allow this lesson to be viewed without purchase/login', 'woothemes-sensei' ) . '<br>';
234
235
		// Output the HTML
236
		echo $html;
237
	} // End lesson_preview_meta_box_content()
238
239
	/**
240
	 * meta_box_save function.
241
	 *
242
	 * @access public
243
	 * @param int $post_id
244
	 * @return integer $post_id
245
	 */
246
	public function meta_box_save ( $post_id ) {
247
248
		// Verify the nonce before proceeding.
249 View Code Duplication
		if ( ( get_post_type( $post_id ) != $this->token ) || !isset(   $_POST[ 'woo_' . $this->token . '_nonce'] )  || ! wp_verify_nonce( $_POST[ 'woo_' . $this->token . '_nonce' ], 'sensei-save-post-meta' ) ) {
250
			return $post_id;
251
		} // End If Statement
252
		// Get the post type object.
253
		$post_type = get_post_type_object( get_post_type( $post_id ) );
254
		// Check if the current user has permission to edit the post.
255
		if ( !current_user_can( $post_type->cap->edit_post, $post_id ) ) {
256
			return $post_id;
257
		} // End If Statement
258
259
		// Check if the current post type is a page
260 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...
261
262
			if ( ! current_user_can( 'edit_page', $post_id ) ) {
263
264
				return $post_id;
265
266
			} // End If Statement
267
		} else {
268
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
269
				return $post_id;
270
			} // End If Statement
271
		} // End If Statement
272
273
		// Save the post meta data fields
274
		if ( isset($this->meta_fields) && is_array($this->meta_fields) ) {
275
276
			foreach ( $this->meta_fields as $meta_key ) {
277
278
				remove_action( 'save_post', array( $this, 'meta_box_save') );
279
				$this->save_post_meta( $meta_key, $post_id );
280
281
			} // End For Loop
282
		} // End If Statement
283
	} // End meta_box_save()
284
285
286
	/**
287
     * Update the lesson quiz and all the post meta
288
	 *
289
	 * @access public
290
	 * @return integer|boolean $post_id or false
291
	 */
292
	public function quiz_update( $post_id ) {
293
		global $post;
294
		// Verify the nonce before proceeding.
295
		if ( ( 'lesson' != get_post_type( $post_id ) )|| !isset(   $_POST[ 'woo_' . $this->token . '_nonce'] )  || ! wp_verify_nonce( $_POST[ 'woo_' . $this->token . '_nonce' ], 'sensei-save-post-meta') ) {
296
			if ( isset($post->ID) ) {
297
				return $post->ID;
298
			} else {
299
				return false;
300
			} // End If Statement
301
		} // End If Statement
302
303
		if( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
304
			return false;
305
		}
306
307
		// Temporarily disable the filter
308
        remove_action( 'save_post', array( $this, 'quiz_update' ) );
309
		// Save the Quiz
310
		$quiz_id = $this->lesson_quizzes( $post_id, 'any');
311
312
		 // Sanitize and setup the post data
313
		$_POST = stripslashes_deep( $_POST );
314
		if ( isset( $_POST[ 'quiz_id' ] ) && ( 0 < absint( $_POST[ 'quiz_id' ] ) ) ) {
315
			$quiz_id = absint( $_POST[ 'quiz_id' ] );
316
		} // End If Statement
317
		$post_title = esc_html( $_POST[ 'post_title' ] );
318
		$post_status = esc_html( $_POST[ 'post_status' ] );
319
		$post_content = '';
320
321
		// Setup Query Arguments
322
		$post_type_args = array(	'post_content' => $post_content,
323
  		    						'post_status' => $post_status,
324
  		    						'post_title' => $post_title,
325
  		    						'post_type' => 'quiz',
326
                                    'post_parent' => $post_id,
327
  		    						);
328
329
		$settings = $this->get_quiz_settings();
330
331
  		// Update or Insert the Lesson Quiz
332
		if ( 0 < $quiz_id ) {
333
			// Update the Quiz
334
			$post_type_args[ 'ID' ] = $quiz_id;
335
		    wp_update_post($post_type_args);
336
337
		    // Update the post meta data
338
		    update_post_meta( $quiz_id, '_quiz_lesson', $post_id );
339
340
		    foreach( $settings as $field ) {
341
		    	if( 'random_question_order' != $field['id'] ) {
342
			    	$value = $this->get_submitted_setting_value( $field );
343
			    	if( isset( $value ) ) {
344
			    		update_post_meta( $quiz_id, '_' . $field['id'], $value );
345
			    	}
346
			    }
347
		    }
348
349
		    // Set the post terms for quiz-type
350
		    wp_set_post_terms( $quiz_id, array( 'multiple-choice' ), 'quiz-type' );
351
		} else {
352
			// Create the Quiz
353
		    $quiz_id = wp_insert_post($post_type_args);
354
355
		    // Add the post meta data WP will add it if it doesn't exist
356
            update_post_meta( $quiz_id, '_quiz_lesson', $post_id );
357
358
		    foreach( $settings as $field ) {
359
		    	if( 'random_question_order' != $field['id'] ) {
360
361
                    //ignore values not posted to avoid
362
                    // overwriting with empty or default values
363
                    // when the values are posted from bulk edit or quick edit
364
                    if( !isset( $_POST[ $field['id'] ] ) ){
365
                        continue;
366
                    }
367
368
			    	$value = $this->get_submitted_setting_value( $field );
369
			    	if( isset( $value ) ) {
370
			    		add_post_meta( $quiz_id, '_' . $field['id'], $value );
371
			    	}
372
			    }
373
		    }
374
375
		    // Set the post terms for quiz-type
376
		    wp_set_post_terms( $quiz_id, array( 'multiple-choice' ), 'quiz-type' );
377
		} // End If Statement
378
379
		// Add default lesson order meta value
380
		$course_id = get_post_meta( $post_id, '_lesson_course', true );
381
		if( $course_id ) {
382
			if( ! get_post_meta( $post_id, '_order_' . $course_id, true ) ) {
383
				update_post_meta( $post_id, '_order_' . $course_id, 0 );
384
			}
385
		}
386
		// Add reference back to the Quiz
387
		update_post_meta( $post_id, '_lesson_quiz', $quiz_id );
388
		// Mark if the Lesson Quiz has questions
389
		$quiz_questions = Sensei()->lesson->lesson_quiz_questions( $quiz_id );
390
		if( 0 < count( $quiz_questions ) ) {
391
			update_post_meta( $post_id, '_quiz_has_questions', '1' );
392
		}
393
		else {
394
			delete_post_meta( $post_id, '_quiz_has_questions' );
395
		}
396
397
		// Restore the previously disabled filter
398
        add_action( 'save_post', array( $this, 'quiz_update' ) );
399
400
	} // End post_updated()
401
402
	public function get_submitted_setting_value( $field = false ) {
403
404
		if( ! $field ) return;
405
406
		$value = false;
407
408
		if( 'quiz_grade_type' == $field['id'] ) {
409
			if( isset( $_POST[ $field['id'] ] ) && 'on' == $_POST[ $field['id'] ] ) {
410
				$value = 'auto';
411
			} else {
412
				$value = 'manual';
413
			}
414
			return $value;
415
		}
416
417
		if ( isset( $_POST[ $field['id'] ] ) ) {
418
			$value = $_POST[ $field['id'] ];
419
		} else {
420
			$value = $field['default'];
421
		}
422
423
		return $value;
424
	}
425
426
	/**
427
	 * save_post_meta function.
428
	 * Saves lesson meta data
429
	 * @access private
430
	 * @param string $post_key (default: '')
431
	 * @param int $post_id (default: 0)
432
	 * @return int|bool meta id or saved status
433
	 */
434
	private function save_post_meta( $post_key = '', $post_id = 0 ) {
435
		// Get the meta key.
436
		$meta_key = '_' . $post_key;
437
438
        //ignore fields are not posted
439
440
        if( !isset( $_POST[ $post_key ] ) ){
441
442
            // except for lesson preview checkbox field
443
            if( 'lesson_preview' == $post_key ){
444
445
                $_POST[ $post_key ] = '';
446
447
            } else {
448
449
                return false;
450
451
            }
452
453
        }
454
455
		// Get the posted data and sanitize it for use as an HTML class.
456 View Code Duplication
		if ( 'lesson_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...
457
			$new_meta_value = esc_html( $_POST[$post_key] );
458
		} else {
459
			$new_meta_value = ( isset( $_POST[$post_key] ) ? sanitize_html_class( $_POST[$post_key] ) : '' );
460
		} // End If Statement
461
462
        // update field with the new value
463
        if( -1 != $new_meta_value  ){
464
            return update_post_meta( $post_id, $meta_key, $new_meta_value );
465
        }
466
467
	} // End save_post_meta()
468
469
	/**
470
	 * lesson_course_meta_box_content function.
471
	 *
472
	 * @access public
473
	 * @return void
474
	 */
475
	public function lesson_course_meta_box_content () {
476
		global $post;
477
		// Setup Lesson Meta Data
478
		$selected_lesson_course = 0;
479
		if ( 0 < $post->ID ) {
480
			$selected_lesson_course = get_post_meta( $post->ID, '_lesson_course', true );
481
		} // End If Statement
482
		// Handle preselected course
483 View Code Duplication
		if ( isset( $_GET[ 'course_id' ] ) && ( 0 < absint( $_GET[ 'course_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...
484
			$selected_lesson_course = absint( $_GET[ 'course_id' ] );
485
		} // End If Statement
486
		// Get the Lesson Posts
487
		$post_args = array(	'post_type' 		=> 'course',
488
							'posts_per_page' 		=> -1,
489
							'orderby'         	=> 'title',
490
    						'order'           	=> 'ASC',
491
    						'post_status'      	=> 'any',
492
    						'suppress_filters' 	=> 0,
493
							);
494
		$posts_array = get_posts( $post_args );
495
		// Buid the HTML to Output
496
		$html = '';
497
		// Nonce
498
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
499
500
        // Select the course for the lesson
501
        $drop_down_args = array(
502
            'name'=>'lesson_course',
503
            'id' => 'lesson-course-options'
504
        );
505
506
        $courses = WooThemes_Sensei_Course::get_all_courses();
507
        $courses_options = array();
508
        foreach( $courses as $course ){
509
            $courses_options[ $course->ID ] = get_the_title( $course ) ;
510
        }
511
        $html .= Sensei_Utils::generate_drop_down( $selected_lesson_course, $courses_options, $drop_down_args );
512
513
        // Course Actions Panel
514
		if ( current_user_can( 'publish_courses' )) {
515
				$html .= '<div id="lesson-course-actions">';
516
					$html .= '<p>';
517
						// Add a course action link
518
						$html .= '<a id="lesson-course-add" href="#course-add" class="lesson-add-course">+ ' . __('Add New Course', 'woothemes-sensei' ) . '</a>';
519
					$html .= '</p>';
520
				$html .= '</div>';
521
				// Add a course input fields
522
				$html .= '<div id="lesson-course-details" class="hidden">';
523
					$html .= '<p>';
524
						// Course Title input
525
						$html .= '<label>' . __( 'Course Title' , 'woothemes-sensei' ) . '</label> ';
526
	  					$html .= '<input type="text" id="course-title" name="course_title" value="" size="25" class="widefat" />';
527
	  					// Course Description input
528
	  					$html .= '<label>' . __( 'Description' , 'woothemes-sensei' ) . '</label> ';
529
	  					$html .= '<textarea rows="10" cols="40" id="course-content" name="course_content" value="" size="300" class="widefat"></textarea>';
530
	  					// Course Prerequisite
531
	  					$html .= '<label>' . __( 'Course Prerequisite' , 'woothemes-sensei' ) . '</label> ';
532
	  					$html .= '<select id="course-prerequisite-options" name="course_prerequisite" class="chosen_select widefat">' . "\n";
533
							$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
534
							foreach ($posts_array as $post_item){
535
								$html .= '<option value="' . esc_attr( absint( $post_item->ID ) ) . '">' . esc_html( $post_item->post_title ) . '</option>' . "\n";
536
							} // End For Loop
537
						$html .= '</select>' . "\n";
538
						// Course Product
539
                        if ( Sensei_WC::is_woocommerce_active() ) {
540
	  						// Get the Products
541
							$select_course_woocommerce_product = get_post_meta( $post_item->ID, '_course_woocommerce_product', true );
0 ignored issues
show
$select_course_woocommerce_product is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
542
543
							$product_args = array(	'post_type' 		=> array( 'product', 'product_variation' ),
544
													'posts_per_page' 		=> -1,
545
													'orderby'         	=> 'title',
546
	    											'order'           	=> 'DESC',
547
	    											'post_status'		=> array( 'publish', 'private', 'draft' ),
548
	    											'tax_query'			=> array(
549
														array(
550
															'taxonomy'	=> 'product_type',
551
															'field'		=> 'slug',
552
															'terms'		=> array( 'variable', 'grouped' ),
553
															'operator'	=> 'NOT IN'
554
														)
555
													),
556
	    											'suppress_filters' 	=> 0
557
													);
558
							$products_array = get_posts( $product_args );
559
							$html .= '<label>' . __( 'WooCommerce Product' , 'woothemes-sensei' ) . '</label> ';
560
	  						$html .= '<select id="course-woocommerce-product-options" name="course_woocommerce_product" class="chosen_select widefat">' . "\n";
561
								$html .= '<option value="-">' . __( 'None', 'woothemes-sensei' ) . '</option>';
562
								$prev_parent_id = 0;
563
								foreach ($products_array as $products_item){
564
565
									if ( 'product_variation' == $products_item->post_type ) {
566
										$product_object = get_product( $products_item->ID );
567
										$parent_id = wp_get_post_parent_id( $products_item->ID );
568
										$product_name = ucwords( woocommerce_get_formatted_variation( $product_object->variation_data, true ) );
569
									} else {
570
										$parent_id = false;
571
										$prev_parent_id = 0;
572
										$product_name = $products_item->post_title;
573
									}
574
575
									// Show variations in groups
576 View Code Duplication
									if( $parent_id && $parent_id != $prev_parent_id ) {
577
										if( 0 != $prev_parent_id ) {
578
											$html .= '</optgroup>';
579
										}
580
										$html .= '<optgroup label="' . get_the_title( $parent_id ) . '">';
581
										$prev_parent_id = $parent_id;
582
									} elseif( ! $parent_id && 0 == $prev_parent_id ) {
583
										$html .= '</optgroup>';
584
									}
585
586
									$html .= '<option value="' . esc_attr( absint( $products_item->ID ) ) . '">' . esc_html( $products_item->post_title ) . '</option>' . "\n";
587
								} // End For Loop
588
							$html .= '</select>' . "\n";
589
						} else {
590
							// Default
591
							$html .= '<input type="hidden" name="course_woocommerce_product" id="course-woocommerce-product-options" value="-" />';
592
						}
593
						// Course Category
594
	  					$html .= '<label>' . __( 'Course Category' , 'woothemes-sensei' ) . '</label> ';
595
	  					$cat_args = array( 'echo' => false, 'hierarchical' => true, 'show_option_none' => __( 'None', 'woothemes-sensei' ), 'taxonomy' => 'course-category', 'orderby' => 'name', 'id' => 'course-category-options', 'name' => 'course_category', 'class' => 'widefat' );
596
						$html .= wp_dropdown_categories(apply_filters('widget_course_categories_dropdown_args', $cat_args)) . "\n";
597
	  					// Save the course action button
598
	  					$html .= '<a title="' . esc_attr( __( 'Save Course', 'woothemes-sensei' ) ) . '" href="#add-course-metadata" class="lesson_course_save button button-highlighted">' . esc_html( __( 'Add Course', 'woothemes-sensei' ) ) . '</a>';
599
						$html .= '&nbsp;&nbsp;&nbsp;';
600
						// Cancel action link
601
						$html .= '<a href="#course-add-cancel" class="lesson_course_cancel">' . __( 'Cancel', 'woothemes-sensei' ) . '</a>';
602
					$html .= '</p>';
603
				$html .= '</div>';
604
			} // End If Statement
605
606
		// Output the HTML
607
		echo $html;
608
	} // End lesson_course_meta_box_content()
609
610
	public function quiz_panel( $quiz_id = 0 ) {
611
612
		$html = wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
613
		$html .= '<div id="add-quiz-main">';
614 View Code Duplication
			if ( 0 == $quiz_id ) {
615
				$html .= '<p>';
616
					// Default message and Add a Quiz button
617
					$html .= esc_html( __( 'Once you have saved your lesson you will be able to add questions.', 'woothemes-sensei' ) );
618
				$html .= '</p>';
619
			}
620
621
			// Quiz Panel CSS Class
622
			$quiz_class = '';
623
			if ( 0 == $quiz_id ) {
624
				$quiz_class = ' class="hidden"';
625
			} // End If Statement
626
			// Build the HTML to Output
627
			$message_class = '';
628
629
			// Setup Questions Query
630
			$questions = array();
631
			if ( 0 < $quiz_id ) {
632
				$questions = $this->lesson_quiz_questions( $quiz_id );
633
			} // End If Statement
634
635
			$question_count = 0;
636 View Code Duplication
			foreach( $questions as $question ) {
637
638
				if( $question->post_type == 'multiple_question' ) {
639
					$question_number = get_post_meta( $question->ID, 'number', true );
640
					$question_count += $question_number;
641
				} else {
642
					++$question_count;
643
				}
644
645
			}
646
647
			// Inner DIV
648
			$html .= '<div id="add-quiz-metadata"' . $quiz_class . '>';
649
650
				// Quiz ID
651
				$html .= '<input type="hidden" name="quiz_id" id="quiz_id" value="' . esc_attr( $quiz_id ) . '" />';
652
653
				// Default Message
654 View Code Duplication
				if ( 0 == $quiz_id ) {
655
					$html .= '<p class="save-note">';
656
						$html .= esc_html( __( 'Please save your lesson in order to add questions to your quiz.', 'woothemes-sensei' ) );
657
					$html .= '</p>';
658
				} // End If Statement
659
660
			$html .= '</div>';
661
662
			// Question Container DIV
663
			$html .= '<div id="add-question-main"' . $quiz_class . '>';
664
				// Inner DIV
665
				$html .= '<div id="add-question-metadata">';
666
667
					// Count of questions
668
					$html .= '<input type="hidden" name="question_counter" id="question_counter" value="' . esc_attr( $question_count ) . '" />';
669
					// Table headers
670
					$html .= '<table class="widefat" id="sortable-questions">
671
								<thead>
672
								    <tr>
673
								        <th class="question-count-column">#</th>
674
								        <th>' . __( 'Question', 'woothemes-sensei' ) . '</th>
675
								        <th style="width:45px;">' . __( 'Grade', 'woothemes-sensei' ) . '</th>
676
								        <th style="width:125px;">' . __( 'Type', 'woothemes-sensei' ) . '</th>
677
								        <th style="width:125px;">' . __( 'Action', 'woothemes-sensei' ) . '</th>
678
								    </tr>
679
								</thead>
680
								<tfoot>
681
								    <tr>
682
									    <th class="question-count-column">#</th>
683
									    <th>' . __( 'Question', 'woothemes-sensei' ) . '</th>
684
									    <th>' . __( 'Grade', 'woothemes-sensei' ) . '</th>
685
									    <th>' . __( 'Type', 'woothemes-sensei' ) . '</th>
686
									    <th>' . __( 'Action', 'woothemes-sensei' ) . '</th>
687
								    </tr>
688
								</tfoot>';
689
690
					$message_class = '';
691
					if ( 0 < $question_count ) { $message_class = 'hidden'; }
692
693
					$html .= '<tbody id="no-questions-message" class="' . esc_attr( $message_class ) . '">';
694
						$html .= '<tr>';
695
							$html .= '<td colspan="5">' . __( 'There are no Questions for this Quiz yet. Please add some below.', 'woothemes-sensei' ) . '</td>';
696
						$html .= '</tr>';
697
					$html .= '</tbody>';
698
699
					if( 0 < $question_count ) {
700
						$html .= $this->quiz_panel_questions( $questions );
701
					}
702
703
					$html .= '</table>';
704
705
					if( ! isset( $this->question_order ) ) {
706
						$this->question_order = '';
707
					}
708
709
					$html .= '<input type="hidden" id="question-order" name="question-order" value="' . $this->question_order . '" />';
710
711
				$html .= '</div>';
712
713
				// Question Action Container DIV
714
				$html .= '<div id="add-question-actions">';
715
716
					$html .= $this->quiz_panel_add();
717
718
				$html .= '</div>';
719
720
			$html .= '</div>';
721
722
		$html .= '</div>';
723
724
		return $html;
725
726
	}
727
728
	public function quiz_panel_questions( $questions = array() ) {
729
		global $quiz_questions;
730
731
		$quiz_questions = $questions;
732
733
		$html = '';
734
735
		if( count( $questions ) > 0 ) {
736
737
			$question_class = '';
738
			$question_counter = 1;
739
740
			foreach ( $questions as $question ) {
741
742
				$question_id = $question->ID;
743
744
				$question_type = Sensei()->question->get_question_type( $question_id );
745
746
				$multiple_data = array();
747
				$question_increment = 1;
748
				if( 'multiple_question' == $question->post_type ) {
749
					$question_type = 'category';
750
751
					$question_category = get_post_meta( $question->ID, 'category', true );
752
					$question_cat = get_term( $question_category, 'question-category' );
753
754
					$question_number = get_post_meta( $question->ID, 'number', true );
755
					$question_increment = $question_number;
756
757
					$multiple_data = array( $question_cat->name, $question_number );
758
				}
759
760
				if( ! $question_type ) {
761
					$question_type = 'multiple-choice';
762
				}
763
764
				// Row with question and actions
765
				$html .= $this->quiz_panel_question( $question_type, $question_counter, $question_id, 'quiz', $multiple_data );
766
				$question_counter += $question_increment;
767
768
				if( isset( $this->question_order ) && strlen( $this->question_order ) > 0 ) { $this->question_order .= ','; }
769
				$this->question_order .= $question_id;
770
			} // End For Loop
771
		}
772
773
		return $html;
774
775
	}
776
777
	public function quiz_panel_question( $question_type = '', $question_counter = 0, $question_id = 0, $context = 'quiz', $multiple_data = array() ) {
778
		global $row_counter,  $quiz_questions;
779
780
		$html = '';
781
782
		$question_class = '';
783
		if( 'quiz' == $context ) {
784
			if( ! $row_counter || ! isset( $row_counter ) ) {
785
				$row_counter = 1;
786
			}
787
			if( $row_counter % 2 ) { $question_class = 'alternate'; }
788
			++$row_counter;
789
		}
790
791
		if( $question_id ) {
792
793
			if( $question_type != 'category' ) {
794
795
				$question_grade = Sensei()->question->get_question_grade( $question_id );
796
797
				$question_media = get_post_meta( $question_id, '_question_media', true );
798
				$question_media_type = $question_media_thumb = $question_media_link = $question_media_title = '';
799
				$question_media_thumb_class = $question_media_link_class = $question_media_delete_class = 'hidden';
800
				$question_media_add_button = __( 'Add file', 'woothemes-sensei' );
801
				if( 0 < intval( $question_media ) ) {
802
					$mimetype = get_post_mime_type( $question_media );
803
					if( $mimetype ) {
804
						$mimetype_array = explode( '/', $mimetype);
805
						if( isset( $mimetype_array[0] ) && $mimetype_array[0] ) {
806
							$question_media_delete_class = '';
807
							$question_media_type = $mimetype_array[0];
808
							if( 'image' == $question_media_type ) {
809
								$question_media_thumb = wp_get_attachment_thumb_url( $question_media );
810
								if( $question_media_thumb ) {
811
									$question_media_thumb_class = '';
812
								}
813
							}
814
							$question_media_url = wp_get_attachment_url( $question_media );
815
							if( $question_media_url ) {
816
								$attachment = get_post( $question_media );
817
								$question_media_title = $attachment->post_title;
818
819
								if( ! $question_media_title ) {
820
									$question_media_filename = basename( $question_media_url );
821
									$question_media_title = $question_media_filename;
822
								}
823
								$question_media_link = '<a class="' . $question_media_type . '" href="' . esc_url( $question_media_url ) . '" target="_blank">' . $question_media_title . '</a>';
824
								$question_media_link_class = '';
825
							}
826
827
							$question_media_add_button = __( 'Change file', 'woothemes-sensei' );
828
						}
829
					}
830
				}
831
832
				$random_order = get_post_meta( $question_id, '_random_order', true );
833
				if( ! $random_order ) {
834
					$random_order = 'yes';
835
				}
836
837
				if( ! $question_type ) { $question_type = 'multiple-choice'; }
838
			}
839
840
			$html .= '<tbody class="' . $question_class . '">';
841
842
				if( 'quiz' == $context ) {
843
					$html .= '<tr>';
844
						if( $question_type != 'category' ) {
845
							$question = get_post( $question_id );
846
							$html .= '<td class="table-count question-number question-count-column"><span class="number">' . $question_counter . '</span></td>';
847
							$html .= '<td>' . esc_html( $question->post_title ) . '</td>';
848
							$html .= '<td class="question-grade-column">' . esc_html( $question_grade ) . '</td>';
849
							$question_types_filtered = ucwords( str_replace( array( '-', 'boolean' ), array( ' ', __( 'True/False', 'woothemes-sensei' ) ), $question_type ) );
850
							$html .= '<td>' . esc_html( $question_types_filtered ) . '</td>';
851
							$html .= '<td><a title="' . esc_attr( __( 'Edit Question', 'woothemes-sensei' ) ) . '" href="#question_' . $question_counter .'" class="question_table_edit">' . esc_html( __( 'Edit', 'woothemes-sensei' ) ) . '</a> <a title="' . esc_attr( __( 'Remove Question', 'woothemes-sensei' ) ) . '" href="#add-question-metadata" class="question_table_delete">' . esc_html( __( 'Remove', 'woothemes-sensei' ) ) . '</a></td>';
852
853
						} else {
854
855
							$end_number = intval( $question_counter ) + intval( $multiple_data[1] ) - 1;
856
							if( $question_counter == $end_number ) {
857
								$row_numbers = $question_counter;
858
							} else {
859
								$row_numbers = $question_counter . ' - ' . $end_number;
860
							}
861
							$row_title = sprintf( __( 'Selected from \'%1$s\' ', 'woothemes-sensei' ), $multiple_data[0] );
862
863
							$html .= '<td class="table-count question-number question-count-column"><span class="number hidden">' . $question_counter . '</span><span class="hidden total-number">' . $multiple_data[1] . '</span><span class="row-numbers">' . esc_html( $row_numbers ) . '</span></td>';
864
							$html .= '<td>' . esc_html( $row_title ) . '</td>';
865
							$html .= '<td class="question-grade-column"></td>';
866
							$html .= '<td><input type="hidden" name="question_id" class="row_question_id" id="question_' . $question_counter . '_id" value="' . $question_id . '" /></td>';
867
							$html .= '<td><a title="' . esc_attr( __( 'Edit Question', 'woothemes-sensei' ) ) . '" href="#question_' . $question_counter .'" class="question_table_edit" style="visibility:hidden;">' . esc_html( __( 'Edit', 'woothemes-sensei' ) ) . '</a> <a title="' . esc_attr( __( 'Remove Question(s)', 'woothemes-sensei' ) ) . '" href="#add-question-metadata" class="question_multiple_delete" rel="' . $question_id . '">' . esc_html( __( 'Remove', 'woothemes-sensei' ) ) . '</a></td>';
868
869
						}
870
					$html .= '</tr>';
871
				}
872
873
				if( $question_type != 'category' ) {
874
875
					$edit_class = '';
876
					if( 'quiz' == $context ) {
877
						$edit_class = 'hidden';
878
					}
879
880
					$question = get_post( $question_id );
881
					$html .= '<tr class="question-quick-edit ' . esc_attr( $edit_class ) . '">';
882
						$html .= '<td colspan="5">';
883
							$html .= '<span class="hidden question_original_counter">' . $question_counter . '</span>';
884
					    	$html .= '<div class="question_required_fields">';
885
886
						    	// Question title
887
						    	$html .= '<div>';
888
							    	$html .= '<label for="question_' . $question_counter . '">' . __( 'Question:', 'woothemes-sensei' ) . '</label> ';
889
							    	$html .= '<input type="text" id="question_' . $question_counter . '" name="question" value="' . esc_attr( htmlspecialchars( $question->post_title ) ) . '" size="25" class="widefat" />';
890
						    	$html .= '</div>';
891
892
						    	// Question description
893
						    	$html .= '<div>';
894
							    	$html .= '<label for="question_' . $question_counter . '_desc">' . __( 'Question Description (optional):', 'woothemes-sensei' ) . '</label> ';
895
						    	$html .= '</div>';
896
							    	$html .= '<textarea id="question_' . $question_counter . '_desc" name="question_description" class="widefat" rows="4">' . esc_textarea( $question->post_content ) . '</textarea>';
897
898
						    	// Question grade
899
						    	$html .= '<div>';
900
							    	$html .= '<label for="question_' . $question_counter . '_grade">' . __( 'Question grade:', 'woothemes-sensei' ) . '</label> ';
901
							    	$html .= '<input type="number" id="question_' . $question_counter . '_grade" class="question_grade small-text" name="question_grade" min="0" value="' . $question_grade . '" />';
902
						    	$html .= '</div>';
903
904
						    	// Random order
905
						    	if( $question_type == 'multiple-choice' ) {
906
						    		$html .= '<div>';
907
						    			$html .= '<label for="' . $question_counter . '_random_order"><input type="checkbox" name="random_order" class="random_order" id="' . $question_counter . '_random_order" value="yes" ' . checked( $random_order, 'yes', false ) . ' /> ' . __( 'Randomise answer order', 'woothemes-sensei' ) . '</label>';
908
						    		$html .= '</div>';
909
						    	}
910
911
						    	// Question media
912
						    	$html .= '<div>';
913
							    	$html .= '<label for="question_' . $question_counter . '_media_button">' . __( 'Question media:', 'woothemes-sensei' ) . '</label><br/>';
914
							    	$html .= '<button id="question_' . $question_counter . '_media_button" class="upload_media_file_button button-secondary" data-uploader_title="' . __( 'Add file to question', 'woothemes-sensei' ) . '" data-uploader_button_text="' . __( 'Add to question', 'woothemes-sensei' ) . '">' . $question_media_add_button . '</button>';
915
							    	$html .= '<button id="question_' . $question_counter . '_media_button_delete" class="delete_media_file_button button-secondary ' . $question_media_delete_class . '">' . __( 'Delete file', 'woothemes-sensei' ) . '</button><br/>';
916
							    	$html .= '<span id="question_' . $question_counter . '_media_link" class="question_media_link ' . $question_media_link_class . '">' . $question_media_link . '</span>';
917
							    	$html .= '<br/><img id="question_' . $question_counter . '_media_preview" class="question_media_preview ' . $question_media_thumb_class . '" src="' . $question_media_thumb . '" /><br/>';
918
							    	$html .= '<input type="hidden" id="question_' . $question_counter . '_media" class="question_media" name="question_media" value="' . $question_media . '" />';
919
						    	$html .= '</div>';
920
921
						    $html .= '</div>';
922
923
						    $html .= $this->quiz_panel_question_field( $question_type, $question_id, $question_counter );
924
925
						    $html .= '<input type="hidden" id="question_' . $question_counter . '_question_type" class="question_type" name="question_type" value="' . $question_type . '" />';
926
							$html .= '<input type="hidden" name="question_id" class="row_question_id" id="question_' . $question_counter . '_id" value="' . $question_id . '" />';
927
928
							if( 'quiz' == $context ) {
929
					    		$html .= '<div class="update-question">';
930
						    		$html .= '<a href="#question-edit-cancel" class="lesson_question_cancel" title="' . esc_attr( __( 'Cancel', 'woothemes-sensei' ) ) . '">' . __( 'Cancel', 'woothemes-sensei' ) . '</a> ';
931
						    		$html .= '<a title="' . esc_attr( __( 'Update Question', 'woothemes-sensei' ) ) . '" href="#add-question-metadata" class="question_table_save button button-highlighted">' . esc_html( __( 'Update', 'woothemes-sensei' ) ) . '</a>';
932
					    		$html .= '</div>';
933
					    	}
934
935
			    		$html .= '</td>';
936
					$html .= '</tr>';
937
				}
938
939
			$html .= '</tbody>';
940
941
		}
942
943
		return $html;
944
	}
945
946
	public function quiz_panel_add( $context = 'quiz' ) {
947
948
949
		$html = '<div id="add-new-question">';
950
951
			$question_types = Sensei()->question->question_types();
952
953
			$question_cats = get_terms( 'question-category', array( 'hide_empty' => false ) );
954
955
			if( 'quiz' == $context ) {
956
	    		$html .= '<h2 class="nav-tab-wrapper add-question-tabs">';
957
	    			$html .= '<a id="tab-new" class="nav-tab nav-tab-active">' . __( 'New Question'  , 'woothemes-sensei' ) . '</a>';
958
	    			$html .= '<a id="tab-existing" class="nav-tab">' . __( 'Existing Questions'  , 'woothemes-sensei' ) . '</a>';
959
                    if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats )  && ! Sensei()->teacher->is_admin_teacher() ) {
960
	    				$html .= '<a id="tab-multiple" class="nav-tab">' . __( 'Category Questions'  , 'woothemes-sensei' ) . '</a>';
961
	    			}
962
	    		$html .= '</h2>';
963
	    	}
964
965
	    	$html .= '<div class="tab-content" id="tab-new-content">';
966
967
	    		if( 'quiz' == $context ) {
968
	    			$html .= '<p><em>' . sprintf( __( 'Add a new question to this quiz - your question will also be added to the %1$squestion bank%2$s.', 'woothemes-sensei' ), '<a href="' . admin_url( 'edit.php?post_type=question' ) . '">', '</a>' ) . '</em></p>';
969
	    		}
970
971
				$html .= '<div class="question">';
972
					$html .= '<div class="question_required_fields">';
973
974
						// Question title
975
						$html .= '<p><label>' . __( 'Question:'  , 'woothemes-sensei' ) . '</label> ';
976
	  					$html .= '<input type="text" id="add_question" name="question" value="" size="25" class="widefat" /></p>';
977
978
						// Question description
979
						$html .= '<p>';
980
							$html .= '<label for="question_desc">' . __( 'Question Description (optional):', 'woothemes-sensei' ) . '</label> ';
981
						$html .= '</p>';
982
						$html .= '<textarea id="question_desc" name="question_description" class="widefat" rows="4"></textarea>';
983
984
	  					// Question type
985
						$html .= '<p><label>' . __( 'Question Type:' , 'woothemes-sensei' ) . '</label> ';
986
						$html .= '<select id="add-question-type-options" name="question_type" class="chosen_select widefat question-type-select">' . "\n";
987 View Code Duplication
							foreach ( $question_types as $type => $label ) {
988
								$html .= '<option value="' . esc_attr( $type ) . '">' . esc_html( $label ) . '</option>' . "\n";
989
							} // End For Loop
990
						$html .= '</select></p>' . "\n";
991
992
						// Question category
993
						if( 'quiz' == $context ) {
994
							if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats ) ) {
995
								$html .= '<p><label>' . __( 'Question Category:' , 'woothemes-sensei' ) . '</label> ';
996
								$html .= '<select id="add-question-category-options" name="question_category" class="chosen_select widefat question-category-select">' . "\n";
997
								$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>' . "\n";
998 View Code Duplication
								foreach( $question_cats as $cat ) {
999
									$html .= '<option value="' . esc_attr( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
1000
								} // End For Loop
1001
								$html .= '</select></p>' . "\n";
1002
							}
1003
						}
1004
1005
	  					// Question grade
1006
						$html .= '<p><label>' . __( 'Question Grade:'  , 'woothemes-sensei' ) . '</label> ';
1007
						$html .= '<input type="number" id="add-question-grade" name="question_grade" class="small-text" min="0" value="1" /></p>' . "\n";
1008
1009
						// Random order
1010
						$html .= '<p class="add_question_random_order">';
1011
			    			$html .= '<label for="add_random_order"><input type="checkbox" name="random_order" class="random_order" id="add_random_order" value="yes" checked="checked" /> ' . __( 'Randomise answer order', 'woothemes-sensei' ) . '</label>';
1012
			    		$html .= '</p>';
1013
1014
			    		// Question media
1015
						$html .= '<p>';
1016
					    	$html .= '<label for="question_add_new_media_button">' . __( 'Question media:', 'woothemes-sensei' ) . '</label><br/>';
1017
					    	$html .= '<button id="question_add_new_media_button" class="upload_media_file_button button-secondary" data-uploader_title="' . __( 'Add file to question', 'woothemes-sensei' ) . '" data-uploader_button_text="' . __( 'Add to question', 'woothemes-sensei' ) . '">' . __( 'Add file', 'woothemes-sensei' ) . '</button>';
1018
					    	$html .= '<button id="question_add_new_media_button_delete" class="delete_media_file_button button-secondary hidden">' . __( 'Delete file', 'woothemes-sensei' ) . '</button><br/>';
1019
					    	$html .= '<span id="question_add_new_media_link" class="question_media_link hidden"></span>';
1020
					    	$html .= '<br/><img id="question_add_new_media_preview" class="question_media_preview hidden" src="" /><br/>';
1021
					    	$html .= '<input type="hidden" id="question_add_new_media" class="question_media" name="question_media" value="" />';
1022
				    	$html .= '</p>';
1023
1024
					$html .= '</div>';
1025
				$html .= '</div>';
1026
1027
				foreach ( $question_types as $type => $label ) {
1028
					$html .= $this->quiz_panel_question_field( $type );
1029
				}
1030
1031
				if( 'quiz' == $context ) {
1032
					$html .= '<div class="add-question">';
1033
			    		$html .= '<a title="' . esc_attr( __( 'Add Question', 'woothemes-sensei' ) ) . '" href="#add-question-metadata" class="add_question_save button button-primary button-highlighted">' . esc_html( __( 'Add Question', 'woothemes-sensei' ) ) . '</a>';
1034
		    		$html .= '</div>';
1035
		    	}
1036
1037
		    $html .= '</div>';
1038
1039
		    if( 'quiz' == $context ) {
1040
1041
			    $html .= '<div class="tab-content hidden" id="tab-existing-content">';
1042
1043
			    	$html .= '<p><em>' . sprintf( __( 'Add an existing question to this quiz from the %1$squestion bank%2$s.', 'woothemes-sensei' ), '<a href="' . admin_url( 'edit.php?post_type=question' ) . '">', '</a>' ) . '</em></p>';
1044
1045
			    	$html .= '<div id="existing-filters" class="alignleft actions">
1046
			    				<select id="existing-status">
1047
			    					<option value="all">' . __( 'All', 'woothemes-sensei' ) . '</option>
1048
			    					<option value="unused">' . __( 'Unused', 'woothemes-sensei' ) . '</option>
1049
			    					<option value="used">' . __( 'Used', 'woothemes-sensei' ) . '</option>
1050
			    				</select>
1051
			    				<select id="existing-type">
1052
			    					<option value="">' . __( 'All Types', 'woothemes-sensei' ) . '</option>';
1053 View Code Duplication
							    	foreach ( $question_types as $type => $label ) {
1054
										$html .= '<option value="' . esc_attr( $type ) . '">' . esc_html( $label ) . '</option>';
1055
									}
1056
    				$html .= '</select>
1057
    							<select id="existing-category">
1058
			    					<option value="">' . __( 'All Categories', 'woothemes-sensei' ) . '</option>';
1059 View Code Duplication
				    				foreach( $question_cats as $cat ) {
1060
										$html .= '<option value="' . esc_attr( $cat->slug ) . '">' . esc_html( $cat->name ) . '</option>';
1061
									}
1062
    				$html .= '</select>
1063
    							<input type="text" id="existing-search" placeholder="' . __( 'Search', 'woothemes-sensei' ) . '" />
1064
    							<a class="button" id="existing-filter-button">' . __( 'Filter', 'woothemes-sensei' ) . '</a>
1065
			    			</div>';
1066
1067
			    	$html .= '<table id="existing-table" class="widefat">';
1068
1069
			    		$html .= '<thead>
1070
									    <tr>
1071
									        <th scope="col" class="column-cb check-column"><input type="checkbox" /></th>
1072
									        <th scope="col">' . __( 'Question', 'woothemes-sensei' ) . '</th>
1073
									        <th scope="col">' . __( 'Type', 'woothemes-sensei' ) . '</th>
1074
									        <th scope="col">' . __( 'Category', 'woothemes-sensei' ) . '</th>
1075
									    </tr>
1076
									</thead>
1077
									<tfoot>
1078
									    <tr>
1079
										    <th scope="col" class="check-column"><input type="checkbox" /></th>
1080
									        <th scope="col">' . __( 'Question', 'woothemes-sensei' ) . '</th>
1081
									        <th scope="col">' . __( 'Type', 'woothemes-sensei' ) . '</th>
1082
									        <th scope="col">' . __( 'Category', 'woothemes-sensei' ) . '</th>
1083
									    </tr>
1084
									</tfoot>';
1085
						$html .= '<tbody id="existing-questions">';
1086
1087
						$questions = $this->quiz_panel_get_existing_questions();
1088
1089
						$row = 1;
1090
						foreach( $questions['questions'] as $question ) {
1091
							$html .= $this->quiz_panel_add_existing_question( $question->ID, $row );
1092
							++$row;
1093
						}
1094
1095
						$html .= '</tbody>';
1096
1097
			    	$html .= '</table>';
1098
1099
			    	$next_class = '';
1100
			    	if( $questions['count'] <= 10 ) {
1101
			    		$next_class = 'hidden';
1102
			    	}
1103
1104
			    	$html .= '<div id="existing-pagination">';
1105
			    		$html .= '<input type="hidden" id="existing-page" value="1" />';
1106
			    		$html .= '<a class="prev no-paging">&larr; ' . __( 'Previous', 'woothemes-sensei') . '</a> <a class="next ' . esc_attr( $next_class ) . '">' . __( 'Next', 'woothemes-sensei') . ' &rarr;</a>';
1107
			    	$html .= '</div>';
1108
1109
			    	$html .= '<div class="existing-actions">';
1110
			    		$html .= '<a title="' . esc_attr( __( 'Add Selected Question(s)', 'woothemes-sensei' ) ) . '" class="add_existing_save button button-primary button-highlighted">' . esc_html( __( 'Add Selected Question(s)', 'woothemes-sensei' ) ) . '</a></p>';
1111
			    	$html .= '</div>';
1112
1113
			    $html .= '</div>';
1114
1115
			    if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats ) ) {
1116
				    $html .= '<div class="tab-content hidden" id="tab-multiple-content">';
1117
1118
				    	$html .= '<p><em>' . sprintf( __( 'Add any number of questions from a specified category. Edit your question categories %1$shere%2$s.', 'woothemes-sensei' ), '<a href="' . admin_url( 'edit-tags.php?taxonomy=question-category&post_type=question' ) . '">', '</a>' ) . '</em></p>';
1119
1120
						$html .= '<p><select id="add-multiple-question-category-options" name="multiple_category" class="chosen_select widefat question-category-select">' . "\n";
1121
						$html .= '<option value="">' . __( 'Select a Question Category', 'woothemes-sensei' ) . '</option>' . "\n";
1122 View Code Duplication
						foreach( $question_cats as $cat ) {
1123
							$html .= '<option value="' . esc_attr( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
1124
						} // End For Loop
1125
						$html .= '</select></p>' . "\n";
1126
1127
						$html .= '<p>' . __( 'Number of questions:', 'woothemes-sensei' ) . ' <input type="number" min="1" value="1" max="1" id="add-multiple-question-count" class="small-text"/>';
1128
1129
						$html .= '<a title="' . esc_attr( __( 'Add Question(s)', 'woothemes-sensei' ) ) . '" class="add_multiple_save button button-primary button-highlighted">' . esc_html( __( 'Add Question(s)', 'woothemes-sensei' ) ) . '</a></p>';
1130
1131
				    $html .= '</div>';
1132
				}
1133
			}
1134
1135
		$html .= '</div>';
1136
1137
		return $html;
1138
	}
1139
1140
	public function quiz_panel_get_existing_questions( $question_status = 'all', $question_type = '', $question_category = '', $question_search = '', $page = 1 ) {
1141
1142
		$args = array(
1143
			'post_type' => 'question',
1144
			'posts_per_page' => 10,
1145
			'post_status' => 'publish',
1146
			'suppress_filters' => 0,
1147
		);
1148
1149
		switch( $question_status ) {
1150
			case 'unused': $quiz_status = 'NOT EXISTS'; break;
1151
			case 'used': $quiz_status = 'EXISTS'; break;
1152
			default: $quiz_status = ''; break;
1153
		}
1154
1155
		if( $quiz_status ) {
1156
			switch( $quiz_status ) {
1157
				case 'EXISTS':
1158
					$args['meta_query'][] = array(
1159
						'key' => '_quiz_id',
1160
						'compare' => $quiz_status,
1161
					);
1162
				break;
1163
1164
				case 'NOT EXISTS':
1165
					$args['meta_query'][] = array(
1166
						'key' => '_quiz_id',
1167
						'value' => 'bug #23268',
1168
						'compare' => $quiz_status,
1169
					);
1170
				break;
1171
			}
1172
		}
1173
1174 View Code Duplication
		if( $question_type ) {
1175
			$args['tax_query'][] = array(
1176
				'taxonomy' => 'question-type',
1177
				'field' => 'slug',
1178
				'terms' => $question_type,
1179
			);
1180
		}
1181
1182 View Code Duplication
		if( $question_category ) {
1183
			$args['tax_query'][] = array(
1184
				'taxonomy' => 'question-category',
1185
				'field' => 'slug',
1186
				'terms' => $question_category,
1187
			);
1188
		}
1189
1190
		if( $question_type && $question_category ) {
1191
			$args['tax_query']['relation'] = 'AND';
1192
		}
1193
1194
		if( $question_search ) {
1195
			$args['s'] = $question_search;
1196
		}
1197
1198
		if( $page ) {
1199
			$args['paged'] = $page;
1200
		}
1201
1202
		$qry = new WP_Query( $args );
1203
1204
        /**
1205
         * Filter existing questions query
1206
         *
1207
         * @since 1.8.0
1208
         *
1209
         * @param WP_Query $wp_query
1210
         */
1211
        $qry = apply_filters( 'sensei_existing_questions_query_results', $qry );
1212
1213
		$questions['questions'] = $qry->posts;
1214
		$questions['count'] = intval( $qry->found_posts );
1215
		$questions['page'] = $page;
1216
1217
		return $questions;
1218
	}
1219
1220
	public function quiz_panel_add_existing_question( $question_id = 0, $row = 1 ) {
1221
1222
		$html = '';
1223
1224
		if( ! $question_id ) {
1225
1226
            return;
1227
1228
        }
1229
1230
		$existing_class = '';
1231
		if( $row % 2 ) {
1232
            $existing_class = 'alternate';
1233
        }
1234
1235
		$question_type = Sensei()->question->get_question_type( $question_id );
1236
1237
		$question_cat_list = strip_tags( get_the_term_list( $question_id, 'question-category', '', ', ', '' ) );
1238
1239
		$html .= '<tr class="' . esc_attr( $existing_class ) . '">
1240
					<td class="cb"><input type="checkbox" value="' . $question_id . '" class="existing-item" /></td>
1241
					<td>' . get_the_title( $question_id ) . '</td>
1242
					<td>' . esc_html( $question_type ) . '</td>
1243
					<td>' . esc_html( $question_cat_list ) . '</td>
1244
				  </tr>';
1245
1246
		return $html;
1247
1248
	}
1249
1250
	public function quiz_panel_filter_existing_questions() {
1251
1252
		$return = '';
1253
1254
		//Add nonce security to the request
1255
		$nonce = '';
1256
		if( isset( $_POST['filter_existing_questions_nonce'] ) ) {
1257
			$nonce = esc_html( $_POST['filter_existing_questions_nonce'] );
1258
		} // End If Statement
1259
1260
		if( ! wp_verify_nonce( $nonce, 'filter_existing_questions_nonce' ) ) {
1261
			die( $return );
1262
		} // End If Statement
1263
1264
		// Parse POST data
1265
		$data = $_POST['data'];
1266
		$question_data = array();
1267
		parse_str( $data, $question_data );
1268
1269
		if( 0 < count( $question_data ) ) {
1270
1271
			$question_status = '';
1272
			if( isset( $question_data['question_status'] ) ) {
1273
				$question_status = $question_data['question_status'];
1274
			}
1275
1276
			$question_type = '';
1277
			if( isset( $question_data['question_type'] ) ) {
1278
				$question_type = $question_data['question_type'];
1279
			}
1280
1281
			$question_category = '';
1282
			if( isset( $question_data['question_category'] ) ) {
1283
				$question_category = $question_data['question_category'];
1284
			}
1285
1286
			$question_search = '';
1287
			if( isset( $question_data['question_search'] ) ) {
1288
				$question_search = $question_data['question_search'];
1289
			}
1290
1291
			$question_page = 1;
1292
			if( isset( $question_data['question_page'] ) ) {
1293
				$question_page = intval( $question_data['question_page'] );
1294
			}
1295
1296
			$questions = $this->quiz_panel_get_existing_questions( $question_status, $question_type, $question_category, $question_search, $question_page );
1297
1298
			$row = 1;
1299
			$html = '';
1300
			foreach( $questions['questions'] as $question ) {
1301
				$html .= $this->quiz_panel_add_existing_question( $question->ID, $row );
1302
				++$row;
1303
			}
1304
1305
			if( ! $html ) {
1306
				$html = '<tr class="alternate">
1307
								<td class="no-results" colspan="4"><em>' . __( 'There are no questions matching your search.', 'woothemes-sensei' ) . '</em></td>
1308
							  </tr>';
1309
			}
1310
1311
			$return['html'] = $html;
1312
			$return['count'] = $questions['count'];
1313
			$return['page'] = $questions['page'];
1314
1315
			wp_send_json( $return );
1316
		}
1317
1318
		die( $return );
1319
	}
1320
1321
	public function quiz_panel_question_field( $question_type = '', $question_id = 0, $question_counter = 0 ) {
1322
1323
		$html = '';
1324
1325
		if( $question_type ) {
1326
1327
			$right_answer = '';
1328
			$wrong_answers = array();
1329
			$answer_order_string = '';
1330
			$answer_order = array();
1331
			if( $question_id ) {
1332
				$right_answer = get_post_meta( $question_id, '_question_right_answer', true);
1333
				$wrong_answers = get_post_meta( $question_id, '_question_wrong_answers', true);
1334
				$answer_order_string = get_post_meta( $question_id, '_answer_order', true );
1335
				$answer_order = array_filter( explode( ',', $answer_order_string ) );
1336
				$question_class = '';
1337
			} else {
1338
				$question_id = '';
1339
				$question_class = 'answer-fields question_required_fields hidden';
1340
			}
1341
1342
			switch ( $question_type ) {
1343
				case 'multiple-choice':
1344
					$html .= '<div class="question_default_fields multiple-choice-answers ' . str_replace( ' hidden', '', $question_class ) . '">';
1345
1346
						$right_answers = (array) $right_answer;
1347
						// Calculate total right answers available (defaults to 1)
1348
						$total_right = 0;
1349
						if( $question_id ) {
1350
							$total_right = get_post_meta( $question_id, '_right_answer_count', true );
1351
						}
1352
						if( 0 == intval( $total_right ) ) {
1353
							$total_right = 1;
1354
						}
1355
						for ( $i = 0; $i < $total_right; $i++ ) {
1356
							if ( !isset( $right_answers[ $i ] ) ) { $right_answers[ $i ] = ''; }
1357
							$right_answer_id = $this->get_answer_id( $right_answers[ $i ] );
1358
							// Right Answer
1359
							$right_answer = '<label class="answer" for="question_' . $question_counter . '_right_answer_' . $i . '"><span>' . __( 'Right:' , 'woothemes-sensei' ) . '</span> <input rel="' . esc_attr( $right_answer_id ) . '" type="text" id="question_' . $question_counter . '_right_answer_' . $i . '" name="question_right_answers[]" value="' . esc_attr( $right_answers[ $i ] ) . '" size="25" class="question_answer widefat" /> <a class="remove_answer_option"></a></label>';
1360
							if( $question_id ) {
1361
								$answers[ $right_answer_id ] = $right_answer;
1362
							} else {
1363
								$answers[] = $right_answer;
1364
							}
1365
						}
1366
1367
				    	// Calculate total wrong answers available (defaults to 4)
1368
				    	$total_wrong = 0;
1369
				    	if( $question_id ) {
1370
				    		$total_wrong = get_post_meta( $question_id, '_wrong_answer_count', true );
1371
				    	}
1372
				    	if( 0 == intval( $total_wrong ) ) {
1373
				    		$total_wrong = 1;
1374
				    	}
1375
1376
                        // Setup Wrong Answer HTML
1377
                        foreach ( $wrong_answers as $i => $answer ){
1378
1379
                            $answer_id = $this->get_answer_id( $answer );
1380
                            $wrong_answer = '<label class="answer" for="question_' . $question_counter . '_wrong_answer_' . $i . '"><span>' . __( 'Wrong:' , 'woothemes-sensei' ) ;
1381
                            $wrong_answer .= '</span> <input rel="' . esc_attr( $answer_id ) . '" type="text" id="question_' . $question_counter . '_wrong_answer_' . $i ;
1382
                            $wrong_answer .= '" name="question_wrong_answers[]" value="' . esc_attr( $answer ) . '" size="25" class="question_answer widefat" /> <a class="remove_answer_option"></a></label>';
1383
                            if( $question_id ) {
1384
1385
                                $answers[ $answer_id ] = $wrong_answer;
1386
1387
                            } else {
1388
1389
                                $answers[] = $wrong_answer;
1390
1391
                            }
1392
1393
                        } // end for each
1394
1395
				    	$answers_sorted = $answers;
1396 View Code Duplication
				    	if( $question_id && count( $answer_order ) > 0 ) {
1397
				    		$answers_sorted = array();
1398
				    		foreach( $answer_order as $answer_id ) {
1399
				    			if( isset( $answers[ $answer_id ] ) ) {
1400
				    				$answers_sorted[ $answer_id ] = $answers[ $answer_id ];
1401
				    				unset( $answers[ $answer_id ] );
1402
				    			}
1403
				    		}
1404
1405
				    		if( count( $answers ) > 0 ) {
1406
						    	foreach( $answers as $id => $answer ) {
1407
						    		$answers_sorted[ $id ] = $answer;
1408
						    	}
1409
						    }
1410
				    	}
1411
1412
						foreach( $answers_sorted as $id => $answer ) {
1413
				    		$html .= $answer;
1414
				    	}
1415
1416
				    	$html .= '<input type="hidden" class="answer_order" name="answer_order" value="' . $answer_order_string . '" />';
1417
				    	$html .= '<span class="hidden right_answer_count">' . $total_right . '</span>';
1418
				    	$html .= '<span class="hidden wrong_answer_count">' . $total_wrong . '</span>';
1419
1420
				    	$html .= '<div class="add_answer_options">';
1421
					    	$html .= '<a class="add_right_answer_option add_answer_option button" rel="' . $question_counter . '">' . __( 'Add right answer', 'woothemes-sensei' ) . '</a>';
1422
					    	$html .= '<a class="add_wrong_answer_option add_answer_option button" rel="' . $question_counter . '">' . __( 'Add wrong answer', 'woothemes-sensei' ) . '</a>';
1423
				    	$html .= '</div>';
1424
1425
                        $html .= $this->quiz_panel_question_feedback( $question_counter, $question_id , 'multiple-choice' );
1426
1427
			    	$html .= '</div>';
1428
				break;
1429
				case 'boolean':
1430
					$html .= '<div class="question_boolean_fields ' . $question_class . '">';
1431
						if( $question_id ) {
1432
							$field_name = 'question_' . $question_id . '_right_answer_boolean';
1433
						} else {
1434
							$field_name = 'question_right_answer_boolean';
1435
							$right_answer = 'true';
1436
						}
1437
						$html .= '<label for="question_' . $question_id . '_boolean_true"><input id="question_' . $question_id . '_boolean_true" type="radio" name="' . $field_name . '" value="true" '. checked( $right_answer, 'true', false ) . ' /> ' . __( 'True', 'woothemes-sensei' ) . '</label>';
1438
						$html .= '<label for="question_' . $question_id . '_boolean_false"><input id="question_' . $question_id . '_boolean_false" type="radio" name="' . $field_name . '" value="false" '. checked( $right_answer, 'false', false ) . ' /> ' . __( 'False', 'woothemes-sensei' ) . '</label>';
1439
1440
                    $html .= $this->quiz_panel_question_feedback( $question_counter, $question_id, 'boolean' );
1441
1442
					$html .= '</div>';
1443
				break;
1444
				case 'gap-fill':
1445
					$gapfill_array = explode( '||', $right_answer );
1446
					if ( isset( $gapfill_array[0] ) ) { $gapfill_pre = $gapfill_array[0]; } else { $gapfill_pre = ''; }
1447
					if ( isset( $gapfill_array[1] ) ) { $gapfill_gap = $gapfill_array[1]; } else { $gapfill_gap = ''; }
1448
					if ( isset( $gapfill_array[2] ) ) { $gapfill_post = $gapfill_array[2]; } else { $gapfill_post = ''; }
1449
					$html .= '<div class="question_gapfill_fields ' . $question_class . '">';
1450
						// Fill in the Gaps
1451
						$html .= '<label>' . __( 'Text before the Gap:' , 'woothemes-sensei' ) . '</label> ';
1452
						$html .= '<input type="text" id="question_' . $question_counter . '_add_question_right_answer_gapfill_pre" name="add_question_right_answer_gapfill_pre" value="' . $gapfill_pre . '" size="25" class="widefat gapfill-field" />';
1453
	  					$html .= '<label>' . __( 'The Gap:' , 'woothemes-sensei' ) . '</label> ';
1454
	  					$html .= '<input type="text" id="question_' . $question_counter . '_add_question_right_answer_gapfill_gap" name="add_question_right_answer_gapfill_gap" value="' . $gapfill_gap . '" size="25" class="widefat gapfill-field" />';
1455
	  					$html .= '<label>' . __( 'Text after the Gap:' , 'woothemes-sensei' ) . '</label> ';
1456
	  					$html .= '<input type="text" id="question_' . $question_counter . '_add_question_right_answer_gapfill_post" name="add_question_right_answer_gapfill_post" value="' . $gapfill_post . '" size="25" class="widefat gapfill-field" />';
1457
	  					$html .= '<label>' . __( 'Preview:' , 'woothemes-sensei' ) . '</label> ';
1458
	  					$html .= '<p class="gapfill-preview">' . $gapfill_pre . '&nbsp;<u>' . $gapfill_gap . '</u>&nbsp;' . $gapfill_post . '</p>';
1459
	  				$html .= '</div>';
1460
				break;
1461 View Code Duplication
				case 'multi-line':
1462
					$html .= '<div class="question_multiline_fields ' . $question_class . '">';
1463
						// Guides for grading
1464
						if( $question_counter ) {
1465
							$field_id = 'question_' . $question_counter . '_add_question_right_answer_multiline';
1466
						} else {
1467
							$field_id = 'add_question_right_answer_multiline';
1468
						}
1469
						$html .= '<label>' . __( 'Guide/Teacher Notes for grading the answer' , 'woothemes-sensei' ) . '</label> ';
1470
						$html .= '<textarea id="' . $field_id . '" name="add_question_right_answer_multiline" rows="4" cols="40" class="widefat">' . $right_answer . '</textarea>';
1471
					$html .= '</div>';
1472
				break;
1473 View Code Duplication
				case 'single-line':
1474
					$html .= '<div class="question_singleline_fields ' . $question_class . '">';
1475
						// Recommended Answer
1476
						if( $question_counter ) {
1477
							$field_id = 'question_' . $question_counter . '_add_question_right_answer_singleline';
1478
						} else {
1479
							$field_id = 'add_question_right_answer_singleline';
1480
						}
1481
						$html .= '<label>' . __( 'Recommended Answer' , 'woothemes-sensei' ) . '</label> ';
1482
						$html .= '<input type="text" id="' . $field_id . '" name="add_question_right_answer_singleline" value="' . $right_answer . '" size="25" class="widefat" />';
1483
					$html .= '</div>';
1484
				break;
1485
				case 'file-upload':
1486
					$html .= '<div class="question_fileupload_fields ' . $question_class . '">';
1487
						if( $question_counter ) {
1488
							$right_field_id = 'question_' . $question_counter . '_add_question_right_answer_fileupload';
1489
							$wrong_field_id = 'question_' . $question_counter . '_add_question_wrong_answer_fileupload';
1490
						} else {
1491
							$right_field_id = 'add_question_right_answer_fileupload';
1492
							$wrong_field_id = 'add_question_wrong_answer_fileupload';
1493
						}
1494
1495
						$wrong_answer = '';
1496
						if( isset( $wrong_answers[0] ) ) {
1497
							$wrong_answer = $wrong_answers[0];
1498
						}
1499
						$html .= '<label>' . __( 'Description for student explaining what needs to be uploaded' , 'woothemes-sensei' ) . '</label> ';
1500
						$html .= '<textarea id="' . $wrong_field_id . '" name="add_question_wrong_answer_fileupload" rows="4" cols="40" class="widefat">' . $wrong_answer . '</textarea>';
1501
1502
						// Guides for grading
1503
						$html .= '<label>' . __( 'Guide/Teacher Notes for grading the upload' , 'woothemes-sensei' ) . '</label> ';
1504
						$html .= '<textarea id="' . $right_field_id . '" name="add_question_right_answer_fileupload" rows="4" cols="40" class="widefat">' . $right_answer . '</textarea>';
1505
					$html .= '</div>';
1506
				break;
1507
			}
1508
		}
1509
1510
		return $html;
1511
	}
1512
1513
	public function quiz_panel_question_feedback( $question_counter = 0, $question_id = 0, $question_type = '' ) {
1514
1515
        // default field name
1516
        $field_name = 'answer_feedback';
1517
        if( 'boolean' == $question_type ){
1518
1519
            $field_name = 'answer_feedback_boolean';
1520
1521
        }elseif( 'multiple-choice' == $question_type ){
1522
1523
            $field_name = 'answer_feedback_multiple_choice';
1524
1525
        }// end if
1526
1527
		if( $question_counter ) {
1528
			$field_name = 'answer_' . $question_counter . '_feedback';
1529
		}
1530
1531
		$feedback = '';
1532
		if( $question_id ) {
1533
			$feedback = get_post_meta( $question_id, '_answer_feedback', true );
1534
		}
1535
1536
		$html = '<p title="' . __( 'This feedback will be automatically displayed to the student once they have completed the quiz.', 'woothemes-sensei' ) . '">';
1537
		$html .= '<label for="' . $field_name . '">' . __( 'Answer Feedback' , 'woothemes-sensei' ) . ':</label>';
1538
		$html .= '<textarea id="' . $field_name . '" name="' . $field_name . '" rows="4" cols="40" class="answer_feedback widefat">' . $feedback . '</textarea>';
1539
		$html .= '</p>';
1540
1541
		return $html;
1542
	}
1543
1544
	public function question_get_answer_id() {
1545
		$data = $_POST['data'];
1546
		$answer_data = array();
1547
		parse_str( $data, $answer_data );
1548
		$answer = $answer_data['answer_value'];
1549
		$answer_id = $this->get_answer_id( $answer );
1550
		echo $answer_id;
1551
		die();
1552
	}
1553
1554
	public function get_answer_id( $answer = '' ) {
1555
1556
		$answer_id = '';
1557
1558
		if( $answer ) {
1559
			$answer_id = md5( $answer );
1560
		}
1561
1562
		return $answer_id;
1563
1564
	}
1565
1566
	/**
1567
	 * lesson_quiz_meta_box_content function.
1568
	 *
1569
	 * @access public
1570
	 * @return void
1571
	 */
1572
	public function lesson_quiz_meta_box_content () {
1573
		global $post;
1574
1575
		// Get quiz panel
1576
		$quiz_id = 0;
1577
		$quizzes = array();
1578
		if ( 0 < $post->ID ) {
1579
			$quiz_id = $this->lesson_quizzes( $post->ID, 'any' );
1580
		}
1581
1582
		echo $this->quiz_panel( $quiz_id );
1583
1584
	} // End lesson_quiz_meta_box_content()
1585
1586
	/**
1587
	 * Quiz settings metabox
1588
	 * @return void
1589
	 */
1590
	public function lesson_quiz_settings_meta_box_content() {
1591
		global $post;
1592
1593
		$html = '';
1594
1595
		// Get quiz panel
1596
		$quiz_id = 0;
1597
		$lesson_id = $post->ID;
1598
		$quizzes = array();
1599
		if ( 0 < $lesson_id ) {
1600
			$quiz_id = $this->lesson_quizzes( $lesson_id, 'any' );
1601
		}
1602
1603
		if( $quiz_id ) {
1604
			$html .= $this->quiz_settings_panel( $lesson_id, $quiz_id );
1605
		} else {
1606
			$html .= '<p><em>' . __( 'There is no quiz for this lesson yet - please add one in the \'Quiz Questions\' box.', 'woothemes-sensei' ) . '</em></p>';
1607
		}
1608
1609
		echo $html;
1610
	}
1611
1612
	public function quiz_settings_panel( $lesson_id = 0, $quiz_id = 0 ) {
1613
1614
1615
		$html = '';
1616
1617
		if( ! $lesson_id && ! $quiz_id ) return $html;
1618
1619
		$settings = $this->get_quiz_settings( $quiz_id );
1620
1621
		$html = Sensei()->admin->render_settings( $settings, $quiz_id, 'quiz-settings' );
1622
1623
		return $html;
1624
1625
	}
1626
1627
	public function get_quiz_settings( $quiz_id = 0 ) {
1628
1629
		$disable_passmark = '';
1630
		$pass_required = get_post_meta( $quiz_id, '_pass_required', true );
1631
		if( ! $pass_required ) {
1632
			$disable_passmark = 'hidden';
1633
		}
1634
1635
		// Setup Questions Query
1636
		$questions = array();
1637
		if ( 0 < $quiz_id ) {
1638
			$questions = $this->lesson_quiz_questions( $quiz_id );
1639
		}
1640
1641
		// Count questions
1642
		$question_count = 0;
1643 View Code Duplication
		foreach( $questions as $question ) {
1644
			if( $question->post_type == 'multiple_question' ) {
1645
				$question_number = get_post_meta( $question->ID, 'number', true );
1646
				$question_count += $question_number;
1647
			} else {
1648
				++$question_count;
1649
			}
1650
		}
1651
1652
		$settings = array(
1653
			array(
1654
				'id' 			=> 'pass_required',
1655
				'label'			=> __( 'Pass required to complete lesson', 'woothemes-sensei' ),
1656
				'description'	=> __( 'The passmark must be achieved before the lesson is complete.', 'woothemes-sensei' ),
1657
				'type'			=> 'checkbox',
1658
				'default'		=> '',
1659
				'checked'		=> 'on',
1660
			),
1661
			array(
1662
				'id' 			=> 'quiz_passmark',
1663
				'label'			=> __( 'Quiz passmark percentage', 'woothemes-sensei' ),
1664
				'description'	=> '',
1665
				'type'			=> 'number',
1666
				'default'		=> 0,
1667
				'placeholder'	=> 0,
1668
				'min'			=> 0,
1669
				'max'			=> 100,
1670
				'class'			=> $disable_passmark,
1671
			),
1672
			array(
1673
				'id' 			=> 'show_questions',
1674
				'label'			=> __( 'Number of questions to show', 'woothemes-sensei' ),
1675
				'description'	=> __( 'Show a random selection of questions from this quiz each time a student views it.', 'woothemes-sensei' ),
1676
				'type'			=> 'number',
1677
				'default'		=> '',
1678
				'placeholder'	=> __( 'All', 'woothemes-sensei' ),
1679
				'min'			=> 1,
1680
				'max'			=> $question_count,
1681
			),
1682
			array(
1683
				'id' 			=> 'random_question_order',
1684
				'label'			=> __( 'Randomise question order', 'woothemes-sensei' ),
1685
				'description'	=> '',
1686
				'type'			=> 'checkbox',
1687
				'default'		=> 'no',
1688
				'checked'		=> 'yes',
1689
			),
1690
			array(
1691
				'id' 			=> 'quiz_grade_type',
1692
				'label'			=> __( 'Grade quiz automatically', 'woothemes-sensei' ),
1693
				'description'	=> __( 'Grades quiz and displays answer explanation immediately after completion. Only applicable if quiz is limited to Multiple Choice, True/False and Gap Fill questions. Questions that have a grade of zero are skipped during autograding.', 'woothemes-sensei' ),
1694
				'type'			=> 'checkbox',
1695
				'default'		=> 'auto',
1696
				'checked'		=> 'auto',
1697
			),
1698
			array(
1699
				'id' 			=> 'enable_quiz_reset',
1700
				'label'			=> __( 'Allow user to retake the quiz', 'woothemes-sensei' ),
1701
				'description'	=> __( 'Enables the quiz reset button.', 'woothemes-sensei' ),
1702
				'type'			=> 'checkbox',
1703
				'default'		=> '',
1704
				'checked'		=> 'on',
1705
			),
1706
		);
1707
1708
		return apply_filters( 'sensei_quiz_settings', $settings );
1709
	}
1710
1711
	/**
1712
	 * enqueue_scripts function.
1713
	 *
1714
	 * @access public
1715
	 * @return void
1716
	 */
1717
	public function enqueue_scripts( $hook ) {
1718
		global  $post_type;
1719
1720
		$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question' ) );
1721
		$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
1722
		$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', 'sensei_analysis', 'sensei_learners', 'sensei_updates', 'woothemes-sensei-settings', 'lesson-order' ) );
1723
1724
		// Test for Write Panel Pages
1725
		if ( ( ( isset( $post_type ) && in_array( $post_type, $allowed_post_types ) ) && ( isset( $hook ) && in_array( $hook, $allowed_post_type_pages ) ) ) || ( isset( $_GET['page'] ) && in_array( $_GET['page'], $allowed_pages ) ) ) {
1726
1727
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
1728
1729
			// Load the lessons script
1730
            wp_enqueue_media();
1731
			wp_enqueue_script( 'sensei-lesson-metadata', Sensei()->plugin_url . 'assets/js/lesson-metadata' . $suffix . '.js', array( 'jquery', 'sensei-core-select2' ,'jquery-ui-sortable' ), Sensei()->version, true );
1732
			wp_enqueue_script( 'sensei-lesson-chosen', Sensei()->plugin_url . 'assets/chosen/chosen.jquery' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
1733
			wp_enqueue_script( 'sensei-chosen-ajax', Sensei()->plugin_url . 'assets/chosen/ajax-chosen.jquery' . $suffix . '.js', array( 'jquery', 'sensei-lesson-chosen' ), Sensei()->version, true );
1734
1735
            // Load the bulk edit screen script
1736
            if( 'edit.php' == $hook && 'lesson'==$_GET['post_type'] ) {
1737
                wp_enqueue_script( 'sensei-lessons-bulk-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-bulk-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version , true);
1738
            }
1739
1740
			// Localise script
1741
			$translation_strings = array( 'right_colon' => __( 'Right:', 'woothemes-sensei' ), 'wrong_colon' => __( 'Wrong:', 'woothemes-sensei' ), 'add_file' => __( 'Add file', 'woothemes-sensei' ), 'change_file' => __( 'Change file', 'woothemes-sensei' ), 'confirm_remove' => __( 'Are you sure you want to remove this question?', 'woothemes-sensei' ), 'confirm_remove_multiple' => __( 'Are you sure you want to remove these questions?', 'woothemes-sensei' ), 'too_many_for_cat' => __( 'You have selected more questions than this category contains - please reduce the number of questions that you are adding.', 'woothemes-sensei' ) );
1742
			$ajax_vars = array( 'lesson_update_question_nonce' => wp_create_nonce( 'lesson_update_question_nonce' ), 'lesson_add_course_nonce' => wp_create_nonce( 'lesson_add_course_nonce' ), 'lesson_update_grade_type_nonce' => wp_create_nonce( 'lesson_update_grade_type_nonce' ), 'lesson_update_question_order_nonce' => wp_create_nonce( 'lesson_update_question_order_nonce' ), 'lesson_update_question_order_random_nonce' => wp_create_nonce( 'lesson_update_question_order_random_nonce' ), 'lesson_add_multiple_questions_nonce' => wp_create_nonce( 'lesson_add_multiple_questions_nonce' ), 'lesson_remove_multiple_questions_nonce' => wp_create_nonce( 'lesson_remove_multiple_questions_nonce' ), 'lesson_add_existing_questions_nonce' => wp_create_nonce( 'lesson_add_existing_questions_nonce' ), 'filter_existing_questions_nonce' => wp_create_nonce( 'filter_existing_questions_nonce' ) );
1743
			$data = array_merge( $translation_strings, $ajax_vars );
1744
			wp_localize_script( 'sensei-lesson-metadata', 'woo_localized_data', $data );
1745
1746
			// Chosen RTL
1747
			if ( is_rtl() ) {
1748
				wp_enqueue_script( 'sensei-chosen-rtl', Sensei()->plugin_url . 'assets/chosen/chosen-rtl' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
1749
			}
1750
1751
		}
1752
1753
	} // End enqueue_scripts()
1754
1755
	/**
1756
	 * Load in CSS styles where necessary.
1757
	 *
1758
	 * @access public
1759
	 * @since  1.4.0
1760
	 * @return void
1761
	 */
1762
	public function enqueue_styles ( $hook ) {
1763
		global  $post_type;
1764
1765
		$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question', 'sensei_message' ) );
1766
		$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
1767
		$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', 'sensei_analysis', 'sensei_learners', 'sensei_updates', 'woothemes-sensei-settings' ) );
1768
1769
		// Test for Write Panel Pages
1770 View Code Duplication
		if ( ( ( isset( $post_type ) && in_array( $post_type, $allowed_post_types ) ) && ( isset( $hook ) && in_array( $hook, $allowed_post_type_pages ) ) ) || ( isset( $_GET['page'] ) && in_array( $_GET['page'], $allowed_pages ) ) ) {
1771
			wp_enqueue_style( 'woothemes-sensei-settings-api', esc_url( Sensei()->plugin_url . 'assets/css/settings.css' ), '', Sensei()->version );
1772
		}
1773
1774
	} // End enqueue_styles()
1775
1776
	/**
1777
	 * Add column headings to the "lesson" post list screen.
1778
	 * @access public
1779
	 * @since  1.0.0
1780
	 * @param  array $defaults
1781
	 * @return array $new_columns
1782
	 */
1783 View Code Duplication
	public function add_column_headings ( $defaults ) {
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...
1784
		$new_columns['cb'] = '<input type="checkbox" />';
1785
		$new_columns['title'] = _x( 'Lesson Title', 'column name', 'woothemes-sensei' );
1786
		$new_columns['lesson-course'] = _x( 'Course', 'column name', 'woothemes-sensei' );
1787
		$new_columns['lesson-prerequisite'] = _x( 'Pre-requisite Lesson', 'column name', 'woothemes-sensei' );
1788
		if ( isset( $defaults['date'] ) ) {
1789
			$new_columns['date'] = $defaults['date'];
1790
		}
1791
		return $new_columns;
1792
	} // End add_column_headings()
1793
1794
	/**
1795
	 * Add data for our newly-added custom columns.
1796
	 * @access public
1797
	 * @since  1.0.0
1798
	 * @param  string $column_name
1799
	 * @param  int $id
1800
	 * @return void
1801
	 */
1802
	public function add_column_data ( $column_name, $id ) {
1803
		global $wpdb, $post;
1804
1805
		switch ( $column_name ) {
1806
			case 'id':
1807
				echo $id;
1808
			break;
1809 View Code Duplication
			case 'lesson-course':
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...
1810
				$lesson_course_id = get_post_meta( $id, '_lesson_course', true);
1811
				if ( 0 < absint( $lesson_course_id ) ) {
1812
					echo '<a href="' . esc_url( get_edit_post_link( absint( $lesson_course_id ) ) ) . '" title="' . esc_attr( sprintf( __( 'Edit %s', 'woothemes-sensei' ), get_the_title( absint( $lesson_course_id ) ) ) ) . '">' . get_the_title( absint( $lesson_course_id ) ) . '</a>';
1813
				} // End If Statement
1814
			break;
1815 View Code Duplication
			case 'lesson-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...
1816
				$lesson_prerequisite_id = get_post_meta( $id, '_lesson_prerequisite', true);
1817
				if ( 0 < absint( $lesson_prerequisite_id ) ) {
1818
					echo '<a href="' . esc_url( get_edit_post_link( absint( $lesson_prerequisite_id ) ) ) . '" title="' . esc_attr( sprintf( __( 'Edit %s', 'woothemes-sensei' ), get_the_title( absint( $lesson_prerequisite_id ) ) ) ) . '">' . get_the_title( absint( $lesson_prerequisite_id ) ) . '</a>';
1819
				} // End If Statement
1820
			break;
1821
			default:
1822
			break;
1823
		} // End Switch Statement
1824
	} // End add_column_data()
1825
1826
	/**
1827
	 * lesson_add_course function.
1828
	 *
1829
	 * @access public
1830
	 * @return void
1831
	 */
1832
	public function lesson_add_course () {
1833
		global $current_user;
1834
		//Add nonce security to the request
1835
		if ( isset($_POST['lesson_add_course_nonce']) ) {
1836
			$nonce = esc_html( $_POST['lesson_add_course_nonce'] );
1837
		} // End If Statement
1838
		if ( ! wp_verify_nonce( $nonce, 'lesson_add_course_nonce' )
1839
            || ! current_user_can( 'edit_lessons' ) ) {
1840
			die('');
1841
		} // End If Statement
1842
		// Parse POST data
1843
		$data = $_POST['data'];
1844
		$course_data = array();
1845
		parse_str($data, $course_data);
1846
		// Save the Course
1847
		$updated = false;
0 ignored issues
show
$updated is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1848
		$current_user = wp_get_current_user();
1849
		$question_data['post_author'] = $current_user->ID;
1850
		$updated = $this->lesson_save_course($course_data);
0 ignored issues
show
It seems like $course_data can also be of type null; however, Sensei_Lesson::lesson_save_course() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1851
		echo $updated;
1852
		die(); // WordPress may print out a spurious zero without this can be particularly bad if using JSON
1853
	} // End lesson_add_course()
1854
1855
	/**
1856
	 * lesson_update_question function.
1857
	 *
1858
	 * @access public
1859
	 * @return void
1860
	 */
1861
	public function lesson_update_question () {
1862
		global $current_user;
1863
		//Add nonce security to the request
1864
		if ( isset($_POST['lesson_update_question_nonce']) ) {
1865
			$nonce = esc_html( $_POST['lesson_update_question_nonce'] );
1866
		} // End If Statement
1867
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_question_nonce' )
1868
            ||  ! current_user_can( 'edit_questions' )) {
1869
1870
			die('');
1871
1872
		} // End If Statement
1873
1874
		// Parse POST data
1875
		// WP slashes all incoming data regardless of Magic Quotes setting (see wp_magic_quotes()), which means that
1876
		// even the $_POST['data'] encoded with encodeURIComponent has it's apostrophes slashed.
1877
		// So first restore the original unslashed apostrophes by removing those slashes
1878
		$data = wp_unslash( $_POST['data'] );
1879
		// Then parse the string to an array (note that parse_str automatically urldecodes all the variables)
1880
		$question_data = array();
1881
		parse_str($data, $question_data);
1882
		// Finally re-slash all elements to ensure consistancy for lesson_save_question()
1883
		$question_data = wp_slash( $question_data );
1884
		// Save the question
1885
		$return = false;
1886
		// Question Save and Delete logic
1887
		if ( isset( $question_data['action'] ) && ( $question_data['action'] == 'delete' ) ) {
1888
			// Delete the Question
1889
			$return = $this->lesson_delete_question($question_data);
1890
		} else {
1891
			// Save the Question
1892
			if ( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1893
				$current_user = wp_get_current_user();
1894
				$question_data['post_author'] = $current_user->ID;
1895
				$question_id = $this->lesson_save_question( $question_data );
1896
				$question_type = Sensei()->question->get_question_type( $question_id );
1897
1898
				$question_count = intval( $question_data['question_count'] );
1899
				++$question_count;
1900
1901
				$return = $this->quiz_panel_question( $question_type, $question_count, $question_id );
1902
			} // End If Statement
1903
		} // End If Statement
1904
1905
		echo $return;
1906
1907
		die();
1908
	} // End lesson_update_question()
1909
1910
	public function lesson_add_multiple_questions() {
1911
1912
		$return = '';
1913
1914
		//Add nonce security to the request
1915
		$nonce = '';
1916
		if( isset( $_POST['lesson_add_multiple_questions_nonce'] ) ) {
1917
			$nonce = esc_html( $_POST['lesson_add_multiple_questions_nonce'] );
1918
		} // End If Statement
1919
1920
		if( ! wp_verify_nonce( $nonce, 'lesson_add_multiple_questions_nonce' )
1921
            || ! current_user_can( 'edit_lessons' ) ) {
1922
			die( $return );
1923
		} // End If Statement
1924
1925
		// Parse POST data
1926
		$data = $_POST['data'];
1927
		$question_data = array();
1928
		parse_str( $data, $question_data );
1929
1930
		if( is_array( $question_data ) ) {
1931
			if( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1932
1933
				$quiz_id = intval( $question_data['quiz_id'] );
1934
				$question_number = intval( $question_data['question_number'] );
1935
				$question_category = intval( $question_data['question_category'] );
1936
1937
				$question_counter = intval( $question_data['question_count'] );
1938
				++$question_counter;
1939
1940
				$cat = get_term( $question_category, 'question-category' );
1941
1942
				$post_data = array(
1943
					'post_content' => '',
1944
					'post_status' => 'publish',
1945
					'post_title' => sprintf( __( '%1$s Question(s) from %2$s', 'woothemes-sensei' ), $question_number, $cat->name ),
1946
					'post_type' => 'multiple_question'
1947
				);
1948
1949
				$multiple_id = wp_insert_post( $post_data );
1950
1951
				if( $multiple_id && ! is_wp_error( $multiple_id ) ) {
1952
					add_post_meta( $multiple_id, 'category', $question_category );
1953
					add_post_meta( $multiple_id, 'number', $question_number );
1954
					add_post_meta( $multiple_id, '_quiz_id', $quiz_id, false );
1955
					add_post_meta( $multiple_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_counter );
1956
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
1957
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
1958
					$return = $this->quiz_panel_question( 'category', $question_counter, $multiple_id, 'quiz', array( $cat->name, $question_number ) );
1959
				}
1960
			}
1961
		}
1962
1963
		echo $return;
1964
1965
		die();
1966
	}
1967
1968
	public function lesson_remove_multiple_questions() {
1969
1970
		//Add nonce security to the request
1971
		$nonce = '';
1972
		if( isset( $_POST['lesson_remove_multiple_questions_nonce'] ) ) {
1973
			$nonce = esc_html( $_POST['lesson_remove_multiple_questions_nonce'] );
1974
		} // End If Statement
1975
1976
		if( ! wp_verify_nonce( $nonce, 'lesson_remove_multiple_questions_nonce' )
1977
        || ! current_user_can( 'edit_lessons' ) ) {
1978
			die('');
1979
		} // End If Statement
1980
1981
		// Parse POST data
1982
		$data = $_POST['data'];
1983
		$question_data = array();
1984
		parse_str( $data, $question_data );
1985
1986
		if( is_array( $question_data ) ) {
1987
			wp_delete_post( $question_data['question_id'], true );
1988
		}
1989
1990
		die( 'Deleted' );
1991
	}
1992
1993
	public function get_question_category_limit() {
1994
1995
		// Set default
1996
		$return = 1;
1997
1998
		// Parse POST data
1999
		$data = $_POST['data'];
2000
		$cat_data = array();
2001
		parse_str( $data, $cat_data );
2002
2003
		if( isset( $cat_data['cat'] ) && '' != $cat_data['cat'] ) {
2004
			$cat = get_term( $cat_data['cat'], 'question-category' );
2005
			if( isset( $cat->count ) ) {
2006
				$return = $cat->count;
2007
			}
2008
		}
2009
2010
		echo $return;
2011
2012
		die('');
2013
	}
2014
2015
	public function lesson_add_existing_questions() {
2016
2017
		//Add nonce security to the request
2018
		$nonce = '';
2019
		if( isset( $_POST['lesson_add_existing_questions_nonce'] ) ) {
2020
			$nonce = esc_html( $_POST['lesson_add_existing_questions_nonce'] );
2021
		} // End If Statement
2022
2023
		if( ! wp_verify_nonce( $nonce, 'lesson_add_existing_questions_nonce' )
2024
        || ! current_user_can( 'edit_lessons' ) ) {
2025
			die('');
2026
		} // End If Statement
2027
2028
		// Parse POST data
2029
		$data = $_POST['data'];
2030
		$question_data = array();
2031
		parse_str( $data, $question_data );
2032
2033
		$return = '';
2034
2035
		if( is_array( $question_data ) ) {
2036
2037
			if( isset( $question_data['questions'] ) && '' != $question_data['questions'] ) {
2038
2039
				$questions = explode( ',', trim( $question_data['questions'], ',' ) );
2040
				$quiz_id = $question_data['quiz_id'];
2041
				$question_count = intval( $question_data['question_count'] );
2042
2043
				foreach( $questions as $question_id ) {
2044
2045
					++$question_count;
2046
2047
					$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2048 View Code Duplication
					if( ! in_array( $quiz_id, $quizzes ) ) {
2049
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2050
						$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2051
						update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2052
			    	}
2053
2054
			    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2055
					$question_type = Sensei()->question->get_question_type( $question_id );
2056
2057
					$return .= $this->quiz_panel_question( $question_type, $question_count, $question_id );
2058
				}
2059
			}
2060
		}
2061
2062
		echo $return;
2063
2064
		die('');
2065
	}
2066
2067 View Code Duplication
	public function lesson_update_grade_type() {
2068
		//Add nonce security to the request
2069
		if ( isset($_POST['lesson_update_grade_type_nonce']) ) {
2070
2071
			$nonce = esc_html( $_POST['lesson_update_grade_type_nonce'] );
2072
2073
		} // End If Statement
2074
2075
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_grade_type_nonce' )
2076
        || ! current_user_can( 'edit_lessons' ) ) {
2077
2078
			die('');
2079
2080
		} // End If Statement
2081
2082
		// Parse POST data
2083
		$data = $_POST['data'];
2084
		$quiz_data = array();
2085
		parse_str($data, $quiz_data);
2086
		update_post_meta( $quiz_data['quiz_id'], '_quiz_grade_type', $quiz_data['quiz_grade_type'] );
2087
		die();
2088
	}
2089
2090
	public function lesson_update_question_order() {
2091
		// Add nonce security to the request
2092
		if ( isset($_POST['lesson_update_question_order_nonce']) ) {
2093
			$nonce = esc_html( $_POST['lesson_update_question_order_nonce'] );
2094
		} // End If Statement
2095
2096
        if ( ! wp_verify_nonce( $nonce, 'lesson_update_question_order_nonce' )
2097
            ||! current_user_can( 'edit_lessons' ) ) {
2098
			die('');
2099
		} // End If Statement
2100
2101
		// Parse POST data
2102
		$data = $_POST['data'];
2103
		$quiz_data = array();
2104
		parse_str($data, $quiz_data);
2105
		if( strlen( $quiz_data['question_order'] ) > 0 ) {
2106
			$questions = explode( ',', $quiz_data['question_order'] );
2107
			$o = 1;
2108
			foreach( $questions as $question_id ) {
2109
				update_post_meta( $question_id, '_quiz_question_order' . $quiz_data['quiz_id'], $quiz_data['quiz_id'] . '000' . $o );
2110
				++$o;
2111
			}
2112
			update_post_meta( $quiz_data['quiz_id'], '_question_order', $questions );
2113
		}
2114
		die();
2115
	}
2116
2117 View Code Duplication
	public function lesson_update_question_order_random() {
2118
		//Add nonce security to the request
2119
		if ( isset($_POST['lesson_update_question_order_random_nonce']) ) {
2120
			$nonce = esc_html( $_POST['lesson_update_question_order_random_nonce'] );
2121
		} // End If Statement
2122
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_question_order_random_nonce' )
2123
            || ! current_user_can( 'edit_lessons' ) ) {
2124
2125
			die('');
2126
2127
		} // End If Statement
2128
		// Parse POST data
2129
		$data = $_POST['data'];
2130
		$quiz_data = array();
2131
		parse_str($data, $quiz_data);
2132
		update_post_meta( $quiz_data['quiz_id'], '_random_question_order', $quiz_data['random_question_order'] );
2133
		die();
2134
	}
2135
2136
	/**
2137
	 * lesson_save_course function.
2138
	 *
2139
	 * @access private
2140
	 * @param array $data (default: array())
2141
	 * @return integer|boolean $course_id or false
2142
	 */
2143
	private function lesson_save_course( $data = array() ) {
2144
		global $current_user;
2145
		$return = false;
2146
		// Setup the course data
2147
		$course_id = 0;
2148
		$course_content = '';
2149
		$course_title = '';
2150
		$course_prerequisite = 0;
0 ignored issues
show
$course_prerequisite is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2151
		$course_category = 0;
0 ignored issues
show
$course_category is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2152 View Code Duplication
		if ( isset( $data[ 'course_id' ] ) && ( 0 < absint( $data[ 'course_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...
2153
			$course_id = absint( $data[ 'course_id' ] );
2154
		} // End If Statement
2155
		if ( isset( $data[ 'course_title' ] ) && ( '' != $data[ 'course_title' ] ) ) {
2156
			$course_title = $data[ 'course_title' ];
2157
		} // End If Statement
2158
		$post_title = $course_title;
2159
		if ( isset($data[ 'post_author' ]) ) {
2160
			$post_author = $data[ 'post_author' ];
0 ignored issues
show
$post_author is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2161
		} else {
2162
			$current_user = wp_get_current_user();
2163
			$post_author = $current_user->ID;
0 ignored issues
show
$post_author is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2164
		} // End If Statement
2165
		$post_status = 'publish';
2166
		$post_type = 'course';
2167
		if ( isset( $data[ 'course_content' ] ) && ( '' != $data[ 'course_content' ] ) ) {
2168
			$course_content = $data[ 'course_content' ];
2169
		} // End If Statement
2170
		$post_content = $course_content;
2171
		// Course Query Arguments
2172
		$post_type_args = array(	'post_content' => $post_content,
2173
  		    						'post_status' => $post_status,
2174
  		    						'post_title' => $post_title,
2175
  		    						'post_type' => $post_type
2176
  		    						);
2177
  		// Only save if there is a valid title
2178
  		if ( $post_title != '' ) {
2179
  		    // Check for prerequisite courses & product id
2180
  		    $course_prerequisite_id = absint( $data[ 'course_prerequisite' ] );
2181
  		    $course_woocommerce_product_id = absint( $data[ 'course_woocommerce_product' ] );
2182
  		    $course_category_id = absint( $data[ 'course_category' ] );
2183
  		    if ( 0 == $course_woocommerce_product_id ) { $course_woocommerce_product_id = '-'; }
2184
  		    // Insert or Update the Lesson Quiz
2185
		    if ( 0 < $course_id ) {
2186
		    	$post_type_args[ 'ID' ] = $course_id;
2187
		    	$course_id = wp_update_post($post_type_args);
2188
		    	update_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2189
		    	update_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2190
		    	if ( 0 < $course_category_id ) {
2191
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2192
		    	} // End If Statement
2193
		    } else {
2194
		    	$course_id = wp_insert_post($post_type_args);
2195
		    	add_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2196
		    	add_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2197
		    	if ( 0 < $course_category_id ) {
2198
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2199
		    	} // End If Statement
2200
		    } // End If Statement
2201
		} // End If Statement
2202
  		// Check that the insert or update saved by testing the post id
2203
  		if ( 0 < $course_id ) {
2204
  			$return = $course_id;
2205
  		} // End If Statement
2206
  		return $return;
2207
  	} // End lesson_save_course()
2208
2209
2210
	/**
2211
	 * lesson_save_question function.
2212
	 *
2213
	 * @access private
2214
	 * @param array $data (default: array())
2215
	 * @return integer|boolean $question_id or false
2216
	 */
2217
	public function lesson_save_question( $data = array(), $context = 'quiz' ) {
2218
		$return = false;
2219
		// Save the Questions
2220
		// Setup the Question data
2221
		$question_id = 0;
2222
		$question_text = '';
2223
		$question_right_answer = '';
2224
		$question_wrong_answers = $question_right_answers = array();
2225
		$question_type = 'multiple-choice';
2226
		$question_category = '';
2227
2228
		// Handle Question Type
2229
		if ( isset( $data[ 'question_type' ] ) && ( '' != $data[ 'question_type' ] ) ) {
2230
			$question_type = $data[ 'question_type' ];
2231
		} // End If Statement
2232
2233
		if ( isset( $data[ 'question_category' ] ) && ( '' != $data[ 'question_category' ] ) ) {
2234
			$question_category = $data[ 'question_category' ];
2235
		} // End If Statement
2236
2237 View Code Duplication
		if ( isset( $data[ 'question_id' ] ) && ( 0 < absint( $data[ 'question_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...
2238
			$question_id = absint( $data[ 'question_id' ] );
2239
		} // End If Statement
2240
		if ( isset( $data[ 'question' ] ) && ( '' != $data[ 'question' ] ) ) {
2241
			$question_text = $data[ 'question' ];
2242
		} // End If Statement
2243
		$post_title = $question_text;
0 ignored issues
show
$post_title is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2244
		// Handle Default Fields (multiple choice)
2245
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answers' ] ) && ( '' != $data[ 'question_right_answers' ] ) ) {
2246
			$question_right_answers = $data[ 'question_right_answers' ];
2247
		} // End If Statement
2248
		elseif ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answer' ] ) && ( '' != $data[ 'question_right_answer' ] ) ) {
2249
			$question_right_answer = $data[ 'question_right_answer' ];
2250
		} // End If Statement
2251
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_wrong_answers' ] ) && ( '' != $data[ 'question_wrong_answers' ] ) ) {
2252
			$question_wrong_answers = $data[ 'question_wrong_answers' ];
2253
		} // End If Statement
2254
		// Handle Boolean Fields - Edit
2255
		if ( 'boolean' == $question_type && isset( $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) && ( '' != $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) ) {
2256
			$question_right_answer = $data[ 'question_' . $question_id . '_right_answer_boolean' ];
2257
		} // End If Statement
2258
		// Handle Boolean Fields - Add
2259
		if ( 'boolean' == $question_type && isset( $data[ 'question_right_answer_boolean' ] ) && ( '' != $data[ 'question_right_answer_boolean' ] ) ) {
2260
			$question_right_answer = $data[ 'question_right_answer_boolean' ];
2261
		} // End If Statement
2262
		// Handle Gap Fill Fields
2263
		if ( 'gap-fill' == $question_type && isset( $data[ 'add_question_right_answer_gapfill_gap' ] ) && '' != $data[ 'add_question_right_answer_gapfill_gap' ] ) {
2264
			$question_right_answer = $data[ 'add_question_right_answer_gapfill_pre' ] . '||' . $data[ 'add_question_right_answer_gapfill_gap' ] . '||' . $data[ 'add_question_right_answer_gapfill_post' ];
2265
		} // End If Statement
2266
		// Handle Multi Line Fields
2267
		if ( 'multi-line' == $question_type && isset( $data[ 'add_question_right_answer_multiline' ] ) && ( '' != $data[ 'add_question_right_answer_multiline' ] ) ) {
2268
			$question_right_answer = $data[ 'add_question_right_answer_multiline' ];
2269
		} // End If Statement
2270
		// Handle Single Line Fields
2271
		if ( 'single-line' == $question_type && isset( $data[ 'add_question_right_answer_singleline' ] ) && ( '' != $data[ 'add_question_right_answer_singleline' ] ) ) {
2272
			$question_right_answer = $data[ 'add_question_right_answer_singleline' ];
2273
		} // End If Statement
2274
		// Handle File Upload Fields
2275
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_right_answer_fileupload' ] ) && ( '' != $data[ 'add_question_right_answer_fileupload' ] ) ) {
2276
			$question_right_answer = $data[ 'add_question_right_answer_fileupload' ];
2277
		} // End If Statement
2278
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_wrong_answer_fileupload' ] ) && ( '' != $data[ 'add_question_wrong_answer_fileupload' ] ) ) {
2279
			$question_wrong_answers = array( $data[ 'add_question_wrong_answer_fileupload' ] );
2280
		} // End If Statement
2281
2282
		// Handle Question Grade
2283
		if ( isset( $data[ 'question_grade' ] ) && ( '' != $data[ 'question_grade' ] ) ) {
2284
			$question_grade = $data[ 'question_grade' ];
2285
		} // End If Statement
2286
2287
		// Handle Answer Feedback
2288
		$answer_feedback = '';
2289
		if ( isset( $data[ 'answer_feedback_boolean' ] ) && !empty( $data[ 'answer_feedback_boolean' ] ) ) {
2290
2291
            $answer_feedback = $data[ 'answer_feedback_boolean' ];
2292
2293
		}elseif( isset( $data[ 'answer_feedback_multiple_choice' ] ) && !empty( $data[ 'answer_feedback_multiple_choice' ] ) ){
2294
2295
            $answer_feedback = $data[ 'answer_feedback_multiple_choice' ];
2296
2297
        }elseif( isset( $data[ 'answer_feedback' ] )  ){
2298
2299
            $answer_feedback = $data[ 'answer_feedback' ];
2300
2301
        } // End If Statement
2302
2303
		$post_title = $question_text;
2304
		$post_author = $data[ 'post_author' ];
0 ignored issues
show
$post_author is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2305
		$post_status = 'publish';
2306
		$post_type = 'question';
2307
		// Handle the extended question text
2308
		if ( isset( $data[ 'question_description' ] ) && ( '' != $data[ 'question_description' ] ) ) {
2309
			$post_content = $data[ 'question_description' ];
2310
		}
2311
		else {
2312
			$post_content = '';
2313
		}
2314
		// Question Query Arguments
2315
		$post_type_args = array(	'post_content' => $post_content,
2316
  		    						'post_status' => $post_status,
2317
  		    						'post_title' => $post_title,
2318
  		    						'post_type' => $post_type
2319
  		    						);
2320
2321
  		// Remove empty values and reindex the array
2322
  		if ( is_array( $question_right_answers ) && 0 < count($question_right_answers) ) {
2323
  			$question_right_answers_array = array_values( array_filter( $question_right_answers, 'strlen' ) );
2324
  			$question_right_answers = array();
2325
2326
  			foreach( $question_right_answers_array as $answer ) {
2327
  				if( ! in_array( $answer, $question_right_answers ) ) {
2328
  					$question_right_answers[] = $answer;
2329
  				}
2330
  			}
2331
  			if ( 0 < count($question_right_answers) ) {
2332
  				$question_right_answer = $question_right_answers;
2333
  			}
2334
  		} // End If Statement
2335
  		$right_answer_count = count( $question_right_answer );
2336
2337
		// Remove empty values and reindex the array
2338
  		if ( is_array( $question_wrong_answers ) ) {
2339
  			$question_wrong_answers_array = array_values( array_filter( $question_wrong_answers, 'strlen' ) );
2340
  			$question_wrong_answers = array();
2341
  		} // End If Statement
2342
2343
  		foreach( $question_wrong_answers_array as $answer ) {
2344
  			if( ! in_array( $answer, $question_wrong_answers ) ) {
2345
  				$question_wrong_answers[] = $answer;
2346
  			}
2347
  		}
2348
2349
  		$wrong_answer_count = count( $question_wrong_answers );
2350
2351
  		// Only save if there is a valid title
2352
  		if ( $post_title != '' ) {
2353
2354
  			// Get Quiz ID for the question
2355
  		    $quiz_id = $data['quiz_id'];
2356
2357
  		    // Get question media
2358
			$question_media = $data['question_media'];
2359
2360
  		    // Get answer order
2361
  		    $answer_order = '';
2362
  		    if( isset( $data['answer_order'] ) ) {
2363
				$answer_order = $data['answer_order'];
2364
			}
2365
2366
			// Get random order selection
2367
			$random_order = 'no';
2368
			if( isset( $data['random_order'] ) ) {
2369
				$random_order = $data['random_order'];
2370
			}
2371
2372
  		    // Insert or Update the question
2373
  		    if ( 0 < $question_id ) {
2374
2375
  		    	$post_type_args[ 'ID' ] = $question_id;
2376
		    	$question_id = wp_update_post( $post_type_args );
2377
2378
		    	// Update poast meta
2379
		    	if( 'quiz' == $context ) {
2380
		    		$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2381
		    		if( ! in_array( $quiz_id, $quizzes ) ) {
2382
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2383
			    	}
2384
		    	}
2385
2386
		    	update_post_meta( $question_id, '_question_grade', $question_grade );
2387
		    	update_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2388
		    	update_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2389
		    	update_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2390
		    	update_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2391
		    	update_post_meta( $question_id, '_question_media', $question_media );
2392
		    	update_post_meta( $question_id, '_answer_order', $answer_order );
2393
		    	update_post_meta( $question_id, '_random_order', $random_order );
2394
2395
		    	if( 'quiz' != $context ) {
2396
		    		wp_set_post_terms( $question_id, array( $question_type ), 'question-type', false );
2397
		    	}
2398
				// Don't store empty value, no point
2399
				if ( !empty($answer_feedback) ) {
2400
					update_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2401
				}
2402
2403
		    } else {
2404
				$question_id = wp_insert_post( $post_type_args );
2405
				$question_count = intval( $data['question_count'] );
2406
				++$question_count;
2407
2408
				// Set post meta
2409 View Code Duplication
				if( 'quiz' == $context ) {
2410
					add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2411
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2412
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2413
				}
2414
2415
				if( isset( $question_grade ) ) {
2416
		    		add_post_meta( $question_id, '_question_grade', $question_grade );
2417
		    	}
2418
		    	add_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2419
		    	add_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2420
		    	add_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2421
		    	add_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2422
		    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2423
		    	add_post_meta( $question_id, '_question_media', $question_media );
2424
		    	add_post_meta( $question_id, '_answer_order', $answer_order );
2425
		    	add_post_meta( $question_id, '_random_order', $random_order );
2426
				// Don't store empty value, no point
2427
				if ( !empty($answer_feedback) ) {
2428
					add_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2429
				}
2430
2431
		    	// Set the post terms for question-type
2432
			    wp_set_post_terms( $question_id, array( $question_type ), 'question-type' );
2433
2434
			    if( $question_category ) {
2435
	    			wp_set_post_terms( $question_id, array( $question_category ), 'question-category' );
2436
	    		}
2437
2438
		    } // End If Statement
2439
		} // End If Statement
2440
  		// Check that the insert or update saved by testing the post id
2441
  		if ( 0 < $question_id ) {
2442
  			$return = $question_id;
2443
  		} // End If Statement
2444
  		return $return;
2445
  	} // End lesson_question_save()
2446
2447
2448
	/**
2449
	 * lesson_delete_question function.
2450
	 *
2451
	 * @access private
2452
	 * @param array $data (default: array())
2453
	 * @return boolean
2454
	 */
2455
	private function lesson_delete_question( $data = array() ) {
2456
2457
		// Get which question to delete
2458
		$question_id = 0;
2459 View Code Duplication
		if ( isset( $data[ 'question_id' ] ) && ( 0 < absint( $data[ 'question_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...
2460
			$question_id = absint( $data[ 'question_id' ] );
2461
		} // End If Statement
2462
		// Delete the question
2463
		if ( 0 < $question_id ) {
2464
			$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2465
2466
			foreach( $quizzes as $quiz_id ) {
2467
				if( $quiz_id == $data['quiz_id'] ) {
2468
					delete_post_meta( $question_id, '_quiz_id', $quiz_id );
2469
				}
2470
			}
2471
2472
			return true;
2473
		} // End If Statement
2474
		return false;
2475
	} // End lesson_delete_question()
2476
2477
2478
	/**
2479
	 * lesson_complexities function.
2480
	 *
2481
	 * @access public
2482
	 * @return array $lesson_complexities
2483
	 */
2484
	public function lesson_complexities() {
2485
2486
		// V2 - make filter for this array
2487
        $lesson_complexities = array( 	'easy' => __( 'Easy', 'woothemes-sensei' ),
2488
									'std' => __( 'Standard', 'woothemes-sensei' ),
2489
									'hard' => __( 'Hard', 'woothemes-sensei' )
2490
									);
2491
2492
		return $lesson_complexities;
2493
2494
	} // End lesson_complexities
2495
2496
2497
	/**
2498
	 * lesson_count function.
2499
	 *
2500
	 * @access public
2501
	 * @param string $post_status (default: 'publish')
2502
	 * @return int
2503
	 */
2504
	public function lesson_count( $post_status = 'publish', $course_id = false ) {
2505
2506
		$post_args = array(	'post_type'         => 'lesson',
2507
							'posts_per_page'    => -1,
2508
//							'orderby'           => 'menu_order date',
2509
//							'order'             => 'ASC',
2510
							'post_status'       => $post_status,
2511
							'suppress_filters'  => 0,
2512
							'fields'            => 'ids',
2513
							);
2514
		if( $course_id ) {
2515
			$post_args['meta_query'][] = array(
2516
				'key' => '_lesson_course',
2517
				'value' => $course_id,
2518
			);
2519
		}
2520
		else {
2521
			// Simple check for connection to a Course
2522
			$post_args['meta_query'][] = array(
2523
				'key' => '_lesson_course',
2524
				'value' => 0,
2525
				'compare' => '>=',
2526
			);
2527
		}
2528
2529
		// Allow WP to generate the complex final query, just shortcut to only do an overall count
2530
//		add_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
2531
		$lessons_query = new WP_Query( apply_filters( 'sensei_lesson_count', $post_args ) );
2532
//		remove_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
2533
2534
		return count( $lessons_query->posts );
2535
	} // End lesson_count()
2536
2537
2538
	/**
2539
	 * lesson_quizzes function.
2540
	 *
2541
	 * @access public
2542
	 * @param int $lesson_id (default: 0)
2543
	 * @param string $post_status (default: 'publish')
2544
	 * @param string $fields (default: 'ids')
2545
	 * @return int $quiz_id
2546
	 */
2547 View Code Duplication
	public function lesson_quizzes( $lesson_id = 0, $post_status = 'any', $fields = 'ids' ) {
2548
2549
		$posts_array = array();
0 ignored issues
show
$posts_array is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2550
2551
		$post_args = array(	'post_type' 		=> 'quiz',
2552
							'posts_per_page' 		=> 1,
2553
							'orderby'         	=> 'title',
2554
    						'order'           	=> 'DESC',
2555
    						'post_parent'      	=> $lesson_id,
2556
    						'post_status'		=> $post_status,
2557
							'suppress_filters' 	=> 0,
2558
							'fields'            => $fields
2559
							);
2560
		$posts_array = get_posts( $post_args );
2561
        $quiz_id = array_shift($posts_array);
2562
2563
		return $quiz_id;
2564
	} // End lesson_quizzes()
2565
2566
2567
	/**
2568
	 * Fetches all the questions for a quiz depending on certain conditions.
2569
     *
2570
     * Determine which questions should be shown depending on:
2571
     * - admin/teacher selected questions to be shown
2572
     * - questions shown to a user previously (saved as asked questions)
2573
     * - limit number of questions lesson setting
2574
	 *
2575
     * @since 1.0
2576
	 * @param int $quiz_id (default: 0)
2577
	 * @param string $post_status (default: 'publish')
2578
	 * @param string $orderby (default: 'meta_value_num title')
2579
	 * @param string $order (default: 'ASC')
2580
     *
2581
	 * @return array $questions { $question type WP_Post }
2582
	 */
2583
	public function lesson_quiz_questions( $quiz_id = 0, $post_status = 'any', $orderby = 'meta_value_num title', $order = 'ASC' ) {
2584
2585
		$quiz_id = (string) $quiz_id;
2586
        $quiz_lesson_id = Sensei()->quiz->get_lesson_id( $quiz_id );
2587
2588
        // setup the user id
2589
        if( is_admin() ) {
2590
            $user_id = isset( $_GET['user'] ) ? $_GET['user'] : '' ;
2591
        } else {
2592
            $user_id = get_current_user_id();
2593
        }
2594
2595
        // get the users current status on the lesson
2596
        $user_lesson_status = Sensei_Utils::user_lesson_status( $quiz_lesson_id, $user_id );
2597
2598
		// Set the default question order if it has not already been set for this quiz
2599
		$this->set_default_question_order( $quiz_id );
2600
2601
		// If viewing quiz on the frontend then show questions in random order if set
2602
		if ( ! is_admin() ) {
2603
			$random_order = get_post_meta( $quiz_id, '_random_question_order', true );
2604
			if( $random_order && $random_order == 'yes' ) {
2605
				$orderby = 'rand';
2606
			}
2607
		}
2608
2609
		// Get all questions and multiple questions
2610
		$question_query_args = array(
2611
			'post_type' 		=> array( 'question', 'multiple_question' ),
2612
			'posts_per_page' 	=> -1,
2613
			'meta_key'        	=> '_quiz_question_order' . $quiz_id,
2614
			'orderby'         	=> $orderby,
2615
			'order'           	=> $order,
2616
			'meta_query'		=> array(
2617
				array(
2618
					'key'       => '_quiz_id',
2619
					'value'     => $quiz_id,
2620
				)
2621
			),
2622
			'post_status'		=> $post_status,
2623
			'suppress_filters' 	=> 0
2624
		);
2625
2626
        //query the questions
2627
		$questions_query = new WP_Query( $question_query_args );
2628
2629
        // Set return array to initially include all items
2630
        $questions = $questions_query->posts;
2631
2632
        // set the questions array that will be manipulated within this function
2633
        $questions_array = $questions_query->posts;
2634
2635
		// If viewing quiz on frontend or in grading then only single questions must be shown
2636
		$selected_questions = false;
2637
		if( ! is_admin() || ( is_admin() && isset( $_GET['page'] ) && 'sensei_grading' == $_GET['page'] && isset( $_GET['user'] ) && isset( $_GET['quiz_id'] ) ) ) {
2638
2639
			// Fetch the questions that the user was asked in their quiz if they have already completed it
2640
			$questions_asked_string = !empty( $user_lesson_status->comment_ID) ? get_comment_meta( $user_lesson_status->comment_ID, 'questions_asked', true ) : false;
2641
			if( !empty($questions_asked_string) ) {
2642
2643
				$selected_questions = explode( ',', $questions_asked_string );
2644
2645
				// Fetch each question in the order in which they were asked
2646
				$questions = array();
2647
				foreach( $selected_questions as $question_id ) {
2648
					if( ! $question_id ) continue;
2649
					$question = get_post( $question_id );
2650
					if( ! isset( $question ) || ! isset( $question->ID ) ) continue;
2651
					$questions[] = $question;
2652
				}
2653
2654
			} else {
2655
2656
				// Otherwise, make sure that we convert all multiple questions into single questions
2657
2658
				$multiple_array = array();
2659
				$existing_questions = array();
2660
2661
				// Set array of questions that already exist so we can prevent duplicates from appearing
2662
				foreach( $questions_array as $question ) {
2663
					if( 'question' != $question->post_type ) continue;
2664
					$existing_questions[] = $question->ID;
2665
				}
2666
2667
				// Include only single questions in the return array
2668
				$questions_loop = $questions_array;
2669
				$questions_array = array();
2670
				foreach( $questions_loop as $k => $question ) {
2671
2672
					// If this is a single question then include it
2673
					if( 'question' == $question->post_type ) {
2674
						$questions_array[] = $question;
2675
					} else {
2676
2677
						// If this is a multiple question then get the specified amount of questions from the specified category
2678
						$question_cat = intval( get_post_meta( $question->ID, 'category', true ) );
2679
						$question_number = intval( get_post_meta( $question->ID, 'number', true ) );
2680
2681
						$qargs = array(
2682
							'post_type' 		=> 'question',
2683
							'posts_per_page' 		=> $question_number,
2684
							'orderby'         	=> $orderby,
2685
							'tax_query'			=> array(
2686
								array(
2687
									'taxonomy'  => 'question-category',
2688
									'field'     => 'term_id',
2689
									'terms'		=> $question_cat
2690
								)
2691
							),
2692
							'post_status'		=> $post_status,
2693
							'suppress_filters' 	=> 0,
2694
							'post__not_in'		=> $existing_questions,
2695
						);
2696
						$cat_questions = get_posts( $qargs );
2697
2698
						// Merge results into return array
2699
						$questions_array = array_merge( $questions_array, $cat_questions );
2700
2701
						// Add selected questions to existing questions array to prevent duplicates from being added
2702
						foreach( $questions_array as $cat_question ) {
2703
							if( in_array( $cat_question->ID, $existing_questions ) ) continue;
2704
							$existing_questions[] = $cat_question->ID;
2705
						}
2706
					}
2707
				}
2708
2709
				// Set return data
2710
				$questions = $questions_array;
2711
			}
2712
		}
2713
2714
		// If user has not already taken the quiz and a limited number of questions are to be shown, then show a random selection of the specified amount of questions
2715
		if( ! $selected_questions ) {
2716
2717
			// Only limit questions like this on the frontend
2718
			if( ! is_admin() ) {
2719
2720
				// Get number of questions to show
2721
				$show_questions = intval( get_post_meta( $quiz_id, '_show_questions', true ) );
2722
				if( $show_questions ) {
2723
2724
					// Get random set of array keys from selected questions array
2725
					$selected_questions = array_rand( $questions_array, $show_questions );
2726
2727
					// Loop through all questions and pick the the ones to be shown based on the random key selection
2728
					$questions = array();
2729
					foreach( $questions_array as $k => $question ) {
2730
2731
						// Random keys will always be an array, unless only one question is to be shown
2732
						if( is_array( $selected_questions ) ) {
2733
							if( in_array( $k, $selected_questions ) ) {
2734
								$questions[] = $question;
2735
							}
2736
						} elseif( 1 == $show_questions ) {
2737
							if ( $selected_questions == $k ) {
2738
								$questions[] = $question;
2739
							}
2740
						}
2741
					}
2742
				}
2743
			}
2744
		}
2745
2746
        // Save the questions that will be asked for the current user
2747
        // this happens only once per user/quiz, unless the user resets the quiz
2748
        if( ! is_admin() ){
2749
2750
            if( $user_lesson_status ) {
2751
2752
                $questions_asked = get_comment_meta($user_lesson_status->comment_ID, 'questions_asked', true);
2753
                if ( empty($questions_asked) && $user_lesson_status) {
2754
2755
                    $questions_asked = array();
2756
                    foreach ($questions as $question) {
2757
2758
                        $questions_asked[] = $question->ID;
2759
2760
                    }
2761
2762
                    // save the questions asked id
2763
                    $questions_asked_csv = implode(',', $questions_asked);
2764
                    update_comment_meta($user_lesson_status->comment_ID, 'questions_asked', $questions_asked_csv);
2765
2766
                }
2767
            }
2768
        }
2769
2770
        /**
2771
         * Filter the questions returned by Sensei_Lesson::lessons_quiz_questions
2772
         *
2773
         * @hooked Sensei_Teacher::allow_teacher_access_to_questions
2774
         * @since 1.8.0
2775
         */
2776
		return apply_filters( 'sensei_lesson_quiz_questions', $questions,  $quiz_id  );
2777
2778
	} // End lesson_quiz_questions()
2779
2780
	/**
2781
	 * Set the default quiz order
2782
	 * @param integer $quiz_id ID of quiz
2783
	 */
2784
	public function set_default_question_order( $quiz_id = 0 ) {
2785
2786
		if( $quiz_id ) {
2787
2788
			$question_order = get_post_meta( $quiz_id, '_question_order', true );
2789
2790
			if( ! $question_order ) {
2791
2792
				$args = array(
2793
					'post_type' 		=> 'question',
2794
					'posts_per_page' 		=> -1,
2795
					'orderby'         	=> 'ID',
2796
					'order'           	=> 'ASC',
2797
					'meta_query'		=> array(
2798
						array(
2799
							'key'       => '_quiz_id',
2800
							'value'     => $quiz_id
2801
						)
2802
					),
2803
					'post_status'		=> 'any',
2804
					'suppress_filters' 	=> 0
2805
				);
2806
				$questions = get_posts( $args );
2807
2808
				$o = 1;
2809
				foreach( $questions as $question ) {
2810
					add_post_meta( $question->ID, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $o, true );
2811
					$o++;
2812
				}
2813
			}
2814
		}
2815
2816
	}
2817
2818
	/**
2819
	 * lesson_image function.
2820
	 *
2821
	 * Handles output of the lesson image
2822
	 *
2823
	 * @access public
2824
	 * @param int $lesson_id (default: 0)
2825
	 * @param string $width (default: '100')
2826
	 * @param string $height (default: '100')
2827
	 * @return string
2828
	 */
2829
	public function lesson_image( $lesson_id = 0, $width = '100', $height = '100', $widget = false ) {
2830
2831
		$html = '';
2832
2833
		// Get Width and Height settings
2834
		if ( ( $width == '100' ) && ( $height == '100' ) ) {
2835
2836
			if ( is_singular( 'lesson' ) ) {
2837
2838
				if ( ! $widget && ! Sensei()->settings->settings[ 'lesson_single_image_enable' ] ) {
2839
2840
					return '';
2841
2842
				} // End If Statement
2843
2844
				$image_thumb_size = 'lesson_single_image';
2845
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2846
				$width = $dimensions['width'];
2847
				$height = $dimensions['height'];
2848
				$crop = $dimensions['crop'];
2849
2850
			} else {
2851
2852
				if ( ! $widget && ! Sensei()->settings->settings[ 'course_lesson_image_enable' ] ) {
2853
2854
					return '';
2855
				} // End If Statement
2856
2857
				$image_thumb_size = 'lesson_archive_image';
2858
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2859
				$width = $dimensions['width'];
2860
				$height = $dimensions['height'];
2861
				$crop = $dimensions['crop'];
2862
2863
			} // End If Statement
2864
2865
		} // End If Statement
2866
2867
		$img_url = '';
2868
2869
		if ( has_post_thumbnail( $lesson_id ) ) {
2870
2871
   			// Get Featured Image
2872
   			$img_url = get_the_post_thumbnail( $lesson_id, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
2873
2874 View Code Duplication
 		} else {
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...
2875
2876
 			// Display Image Placeholder if none
2877
			if ( Sensei()->settings->settings[ 'placeholder_images_enable' ] ) {
2878
2879
                $img_url = apply_filters( 'sensei_lesson_placeholder_image_url', '<img src="http://placehold.it/' . $width . 'x' . $height . '" class="woo-image thumbnail alignleft" />' );
2880
2881
			} // End If Statement
2882
2883
		} // End If Statement
2884
2885
		$html .= '<a href="' . get_permalink( $lesson_id ) . '" title="' . esc_attr( get_post_field( 'post_title', $lesson_id ) ) . '">' . $img_url . '</a>';
2886
2887
		return $html;
2888
2889
	} // End lesson_image()
2890
2891
    /**
2892
     * Ooutpu the lesson image
2893
     *
2894
     * @since 1.9.0
2895
     * @param integer $lesson_id
2896
     */
2897
    public static function the_lesson_image( $lesson_id = 0 ){
2898
2899
        echo Sensei()->lesson->lesson_image( $lesson_id );
2900
2901
    }
2902
2903
	/**
2904
	 * Returns the the lesson excerpt.
2905
	 *
2906
	 * @param WP_Post $lesson
2907
     * @param bool $add_p_tags should the excerpt be wrapped by calling wpautop()
2908
	 * @return string
2909
	 */
2910
	public static function lesson_excerpt( $lesson = null, $add_p_tags = true ) {
2911
		$html = '';
2912
		if ( is_a( $lesson, 'WP_Post' ) && 'lesson' == $lesson->post_type ) {
2913
2914
            $excerpt =  $lesson->post_excerpt;
2915
2916
            // if $add_p_tags true wrap with <p> else return the excerpt as is
2917
            $html =  $add_p_tags ? wpautop( $excerpt ) : $excerpt;
2918
2919
		}
2920
		return apply_filters( 'sensei_lesson_excerpt', $html );
2921
2922
	} // End lesson_excerpt()
2923
2924
    /**
2925
     * Returns the course for a given lesson
2926
     *
2927
     * @since 1.7.4
2928
     * @access public
2929
     *
2930
     * @param int $lesson_id
2931
     * @return int|bool $course_id or bool when nothing is found.
2932
     */
2933
     public function get_course_id( $lesson_id ){
2934
2935
         if( ! isset( $lesson_id ) || empty( $lesson_id )
2936
         ||  'lesson' != get_post_type( $lesson_id ) ){
2937
             return false;
2938
         }
2939
2940
         $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true);
2941
2942
         // make sure the course id is valid
2943
         if( empty( $lesson_course_id )
2944
             || is_array( $lesson_course_id )
2945
             || intval( $lesson_course_id ) < 1
2946
             || 'course' != get_post_type( $lesson_course_id ) ){
2947
2948
             return false;
2949
2950
         }
2951
2952
         return $lesson_course_id;
2953
2954
     }// en get_course_id
2955
2956
    /**
2957
     * Add the admin all lessons screen edit options.
2958
     *
2959
     * The fields in this function work for both quick and bulk edit. The ID attributes is used
2960
     * by bulk edit javascript in the front end to retrieve the new values set byt the user. Then
2961
     * name attribute is will be used by the quick edit and submitted via standard POST. This
2962
     * will use this classes save_post_meta function to save the new field data.
2963
     *
2964
     * @hooked quick_edit_custom_box
2965
     * @hooked bulk_edit_custom_box
2966
     *
2967
     * @since 1.8.0
2968
     *
2969
     * @param string $column_name
2970
     * @param string $post_type
2971
     * @return void
2972
     */
2973
    public function all_lessons_edit_fields( $column_name, $post_type ) {
2974
2975
        // only show these options ont he lesson post type edit screen
2976
        if( 'lesson' != $post_type || 'lesson-course' != $column_name
2977
            || ! current_user_can( 'edit_lessons' ) ) {
2978
            return;
2979
        }
2980
2981
        ?>
2982
        <fieldset class="sensei-edit-field-set inline-edit-lesson">
2983
            <div class="sensei-inline-edit-col column-<?php echo $column_name ?>">
2984
                    <?php
2985
                    echo '<h4>' . __('Lesson Information', 'woothemes-sensei') . '</h4>';
2986
                    // create a nonce field to be  used as a security measure when saving the data
2987
                    wp_nonce_field( 'bulk-edit-lessons', '_edit_lessons_nonce' );
2988
                    wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce'  );
2989
2990
                    // unchanged option - we need this in because
2991
                    // the default option in bulk edit should not be empty. If it is
2992
                    // the user will erase data they didn't want to touch.
2993
                    $no_change_text = '-- ' . __('No Change', 'woothemes-sensei') . ' --';
2994
2995
                    //
2996
                    //course selection
2997
                    //
2998
                    $courses =  WooThemes_Sensei_Course::get_all_courses();
2999
                    $course_options = array();
3000
                    if ( count( $courses ) > 0 ) {
3001
                        foreach ($courses as $course ){
3002
                            $course_options[ $course->ID ] = get_the_title( $course->ID );
3003
                        }
3004
                    }
3005
                    //pre-append the no change option
3006
                    $course_options['-1']=  $no_change_text;
3007
                    $course_attributes = array( 'name'=> 'lesson_course', 'id'=>'sensei-edit-lesson-course' , 'class'=>' ' );
3008
                    $course_field =  Sensei_Utils::generate_drop_down( '-1', $course_options, $course_attributes );
3009
                    echo $this->generate_all_lessons_edit_field( __('Lesson Course', 'woothemes-sensei'),   $course_field  );
3010
3011
                    //
3012
                    // lesson complexity selection
3013
                    //
3014
                    $lesson_complexities =  $this->lesson_complexities();
3015
                    //pre-append the no change option
3016
                    $lesson_complexities['-1']=  $no_change_text;
3017
                    $complexity_dropdown_attributes = array( 'name'=> 'lesson_complexity', 'id'=>'sensei-edit-lesson-complexity' , 'class'=>' ');
3018
                    $complexity_filed =  Sensei_Utils::generate_drop_down( '-1', $lesson_complexities, $complexity_dropdown_attributes );
3019
                    echo $this->generate_all_lessons_edit_field( __('Lesson Complexity', 'woothemes-sensei'),   $complexity_filed  );
3020
3021
                    ?>
3022
3023
                    <h4><?php _e('Quiz Settings', 'woothemes-sensei'); ?> </h4>
3024
3025
                    <?php
3026
3027
                    //
3028
                    // Lesson require pass to complete
3029
                    //
3030
                    $pass_required_options = array(
3031
                        '-1' => $no_change_text,
3032
                         '0' => __('No','woothemes'),
3033
                         '1' => __('Yes','woothemes'),
3034
                    );
3035
3036
                    $pass_required_select_attributes = array( 'name'=> 'pass_required',
3037
                                                                'id'=> 'sensei-edit-lesson-pass-required',
3038
                                                                'class'=>' '   );
3039
                    $require_pass_field =  Sensei_Utils::generate_drop_down( '-1', $pass_required_options, $pass_required_select_attributes, false );
3040
                    echo $this->generate_all_lessons_edit_field( __('Pass required', 'woothemes-sensei'),   $require_pass_field  );
3041
3042
                    //
3043
                    // Quiz pass percentage
3044
                    //
3045
                    $quiz_pass_percentage_field = '<input name="quiz_passmark" id="sensei-edit-quiz-pass-percentage" type="number" />';
3046
                    echo $this->generate_all_lessons_edit_field( __('Pass Percentage', 'woothemes-sensei'), $quiz_pass_percentage_field  );
3047
3048
                    //
3049
                    // Enable quiz reset button
3050
                    //
3051
                    $quiz_reset_select__options = array(
3052
                        '-1' => $no_change_text,
3053
                        '0' => __('No','woothemes'),
3054
                        '1' => __('Yes','woothemes'),
3055
                    );
3056
                    $quiz_reset_name_id = 'sensei-edit-enable-quiz-reset';
3057
                    $quiz_reset_select_attributes = array( 'name'=> 'enable_quiz_reset', 'id'=>$quiz_reset_name_id, 'class'=>' ' );
3058
                    $quiz_reset_field =  Sensei_Utils::generate_drop_down( '-1', $quiz_reset_select__options, $quiz_reset_select_attributes, false );
3059
                    echo $this->generate_all_lessons_edit_field( __('Enable quiz reset button', 'woothemes-sensei'), $quiz_reset_field  );
3060
3061
                    ?>
3062
            </div>
3063
        </fieldset>
3064
    <?php
3065
    }// all_lessons_edit_fields
3066
3067
    /**
3068
     * Create the html for the edit field
3069
     *
3070
     * Wraps the passed in field and title combination with the correct html.
3071
     *
3072
     * @since 1.8.0
3073
     *
3074
     * @param string $title that will stand to the left of the field.
3075
     * @param string $field type markup for the field that must be wrapped.
3076
     * @return string $field_html
3077
     */
3078
    public function generate_all_lessons_edit_field( $title  ,$field ){
3079
3080
        $html = '';
3081
        $html = '<div class="inline-edit-group" >';
3082
        $html .=  '<span class="title">'. $title .'</span> ';
3083
        $html .= '<span class="input-text-wrap">';
3084
        $html .= $field;
3085
        $html .= '</span>';
3086
        $html .= '</label></div>';
3087
3088
        return $html ;
3089
3090
    }//end generate_all_lessons_edit_field
3091
3092
    /**
3093
     * Respond to the ajax call from the bulk edit save function. This comes
3094
     * from the admin all lesson screen.
3095
     *
3096
     * @since 1.8.0
3097
     * @return void
3098
     */
3099
    function save_all_lessons_edit_fields() {
3100
3101
        // verify all the data before attempting to save
3102
        if( ! isset( $_POST['security'] ) || ! check_ajax_referer( 'bulk-edit-lessons', 'security' )
3103
            ||  empty( $_POST[ 'post_ids' ] )  || ! is_array( $_POST[ 'post_ids' ] ) ) {
3104
            die();
3105
        }
3106
3107
        // get our variables
3108
        $new_course = sanitize_text_field(  $_POST['sensei_edit_lesson_course'] );
3109
        $new_complexity = sanitize_text_field(  $_POST['sensei_edit_complexity'] );
3110
        $new_pass_required = sanitize_text_field(  $_POST['sensei_edit_pass_required'] );
3111
        $new_pass_percentage = sanitize_text_field(  $_POST['sensei_edit_pass_percentage'] );
3112
        $new_enable_quiz_reset = sanitize_text_field(  $_POST['sensei_edit_enable_quiz_reset'] );
3113
        // store the values for all selected posts
3114
        foreach( $_POST[ 'post_ids' ] as $lesson_id ) {
3115
3116
            // get the quiz id needed for the quiz meta
3117
            $quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
3118
3119
            // do not save the items if the value is -1 as this
3120
            // means it was not changed
3121
3122
            // update lesson course
3123
            if( -1 != $new_course ){
3124
                update_post_meta( $lesson_id, '_lesson_course', $new_course );
3125
            }
3126
            // update lesson complexity
3127
            if( -1 != $new_complexity ){
3128
                update_post_meta( $lesson_id, '_lesson_complexity', $new_complexity );
3129
            }
3130
3131
            // Quiz Related settings
3132
            if( isset( $quiz_id) && 0 < intval( $quiz_id ) ) {
3133
3134
                // update pass required
3135
                if (-1 != $new_pass_required) {
3136
3137
                    $checked = $new_pass_required  ? 'on' : '';
3138
                    update_post_meta($quiz_id, '_pass_required', $checked);
3139
                    unset( $checked );
3140
                }
3141
3142
                // update pass percentage
3143
                if( !empty( $new_pass_percentage) && is_numeric( $new_pass_percentage ) ){
3144
3145
                        update_post_meta($quiz_id, '_quiz_passmark', $new_pass_percentage);
3146
3147
                }
3148
3149
                //
3150
                // update enable quiz reset
3151
                //
3152
                if (-1 != $new_enable_quiz_reset ) {
3153
3154
                    $checked = $new_enable_quiz_reset ? 'on' : ''  ;
3155
                    update_post_meta($quiz_id, '_enable_quiz_reset', $checked);
3156
                    unset( $checked );
3157
3158
                }
3159
3160
3161
            } // end if quiz
3162
3163
        }// end for each
3164
3165
        die();
3166
3167
    } // end save_all_lessons_edit_fields
3168
3169
    /**
3170
     * Loading the quick edit fields defaults.
3171
     *
3172
     * This function will localise the default values along with the script that will
3173
     * add these values to the inputs.
3174
     *
3175
     * NOTE: this function runs for each row in the edit column
3176
     *
3177
     * @since 1.8.0
3178
     * @return void
3179
     */
3180
    public function set_quick_edit_admin_defaults( $column_name, $post_id ){
3181
3182
        if( 'lesson-course' != $column_name ){
3183
            return;
3184
        }
3185
        // load the script
3186
        $suffix = defined( 'SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
3187
        wp_enqueue_script( 'sensei-lesson-quick-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-quick-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
3188
3189
        // setup the values for all meta fields
3190
        $data = array();
3191
        foreach( $this->meta_fields as $field ){
3192
3193
            $data[$field] =  get_post_meta( $post_id, '_'.$field, true );
3194
3195
        }
3196
        // add quiz meta fields
3197
        $quiz_id = Sensei()->lesson->lesson_quizzes( $post_id );
3198
        foreach( Sensei()->quiz->meta_fields as $field ){
3199
3200
            $data[$field] =  get_post_meta( $quiz_id, '_'.$field, true );
3201
3202
        }
3203
3204
        wp_localize_script( 'sensei-lesson-quick-edit', 'sensei_quick_edit_'.$post_id, $data );
3205
3206
    }// end quick edit admin defaults
3207
3208
    /**
3209
     * Filter the classes for lessons on the single course page.
3210
     *
3211
     * Adds the nesecary classes depending on the user data
3212
     *
3213
     * @since 1.9.0
3214
     * @param array $classes
3215
     * @return array $classes
3216
     */
3217
    public static function single_course_lessons_classes( $classes ){
3218
3219
        if(  is_singular('course') ){
3220
3221
            global $post;
3222
            $course_id = $post->ID;
3223
3224
            $lesson_classes = array( 'course', 'post' );
3225
            if ( is_user_logged_in() ) {
3226
3227
                // Check if Lesson is complete
3228
                $single_lesson_complete = Sensei_Utils::user_completed_lesson( get_the_ID(), get_current_user_id() );
3229
                if ( $single_lesson_complete ) {
3230
3231
                    $lesson_classes[] = 'lesson-completed';
3232
3233
                } // End If Statement
3234
3235
            } // End If Statement
3236
3237
            $is_user_taking_course = Sensei_Utils::user_started_course( $course_id, get_current_user_id() );
3238
            if (  Sensei_Utils::is_preview_lesson( get_the_ID() ) && !$is_user_taking_course ) {
3239
3240
                $lesson_classes[] = 'lesson-preview';
3241
3242
            }
3243
3244
            $classes = array_merge( $classes, $lesson_classes  );
3245
3246
        }
3247
3248
        return $classes;
3249
3250
    }// end single_course_lessons_classes
3251
3252
    /**
3253
     * Output the lesson meta for the given lesson
3254
     *
3255
     * @since 1.9.0
3256
     * @param $lesson_id
3257
     */
3258
    public static function the_lesson_meta( $lesson_id ){
3259
3260
        global $wp_query;
3261
        $loop_lesson_number = $wp_query->current_post + 1;
3262
3263
        $course_id = Sensei()->lesson->get_course_id( $lesson_id );
3264
        $single_lesson_complete = false;
3265
        $is_user_taking_course = Sensei_Utils::user_started_course( $course_id, get_current_user_id() );
3266
3267
        // Get Lesson data
3268
        $complexity_array = Sensei()->lesson->lesson_complexities();
3269
3270
        $lesson_complexity = get_post_meta( $lesson_id, '_lesson_complexity', true );
3271
        if ( '' != $lesson_complexity ) {
3272
3273
            $lesson_complexity = $complexity_array[$lesson_complexity];
3274
3275
        }
3276
        $user_info = get_userdata( absint( get_post()->post_author ) );
3277
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id);
3278
        $preview_label = '';
3279
        if ( $is_preview && !$is_user_taking_course ) {
3280
3281
            $preview_label = Sensei()->frontend->sensei_lesson_preview_title_text( $lesson_id);
3282
            $preview_label = '<span class="preview-heading">' . $preview_label . '</span>';
3283
3284
        }
3285
3286
3287
        $count_markup= '';
3288
        /**
3289
         * Filter for if you want the $lesson_count to show next to the lesson.
3290
         *
3291
         * @since 1.0
3292
         * @param bool default false.
3293
         */
3294
        if( apply_filters( 'sensei_show_lesson_numbers', false ) ) {
3295
3296
            $count_markup =  '<span class="lesson-number">' . $loop_lesson_number. '</span>';
3297
3298
        }
3299
3300
        $heading_link_title = sprintf( __( 'Start %s', 'woothemes-sensei' ), get_the_title( $lesson_id ) );
3301
3302
        ?>
3303
        <header>
3304
            <h2>
3305
                <a href="<?php echo esc_url_raw( get_permalink( $lesson_id ) ) ?>"
3306
                   title="<?php esc_attr_e( $heading_link_title ) ?>" >
3307
                    <?php echo $count_markup. get_the_title( $lesson_id ) . $preview_label; ?>
3308
                </a>
3309
            </h2>
3310
3311
            <p class="lesson-meta">
3312
3313
                <?php
3314
3315
                $meta_html = '';
3316
                $user_lesson_status = Sensei_Utils::user_lesson_status( get_the_ID(), get_current_user_id() );
3317
3318
                $lesson_length = get_post_meta( $lesson_id, '_lesson_length', true );
3319 View Code Duplication
                if ( '' != $lesson_length ) {
3320
3321
                    $meta_html .= '<span class="lesson-length">' .  __( 'Length: ', 'woothemes-sensei' ) . $lesson_length . __( ' minutes', 'woothemes-sensei' ) . '</span>';
3322
3323
                }
3324
3325
                if ( Sensei()->settings->get( 'lesson_author' ) ) {
3326
3327
                    $meta_html .= '<span class="lesson-author">' .  __( 'Author: ', 'woothemes-sensei' ) . '<a href="' . get_author_posts_url( absint( get_post()->post_author ) ) . '" title="' . esc_attr( $user_info->display_name ) . '">' . esc_html( $user_info->display_name ) . '</a></span>';
3328
3329
                } // End If Statement
3330
                if ( '' != $lesson_complexity ) {
3331
3332
                    $meta_html .= '<span class="lesson-complexity">' .  __( 'Complexity: ', 'woothemes-sensei' ) . $lesson_complexity .'</span>';
3333
3334
                }
3335
3336 View Code Duplication
                if ( $single_lesson_complete ) {
3337
3338
                    $meta_html .= '<span class="lesson-status complete">' .__( 'Complete', 'woothemes-sensei' ) .'</span>';
3339
3340
                } elseif ( $user_lesson_status ) {
3341
3342
                    $meta_html .= '<span class="lesson-status in-progress">' . __( 'In Progress', 'woothemes-sensei' ) .'</span>';
3343
3344
                } // End If Statement
3345
3346
                echo $meta_html;
3347
3348
                ?>
3349
3350
            </p> <!-- lesson meta -->
3351
3352
        </header>
3353
3354
    <?php
3355
3356
    } // end the_lesson_meta
3357
3358
    /**
3359
     * Output the lessons thumbnail
3360
     *
3361
     * 1.9.0
3362
     *
3363
     * @param $lesson_id
3364
     */
3365
    public static function the_lesson_thumbnail( $lesson_id ){
3366
3367
        if( empty( $lesson_id ) ){
3368
3369
            $lesson_id = get_the_ID();
3370
3371
        }
3372
3373
        if( 'lesson' != get_post_type( $lesson_id ) ){
3374
            return;
3375
        }
3376
3377
        echo Sensei()->lesson->lesson_image( $lesson_id );
3378
    }
3379
3380
3381
    /**
3382
     * Alter the sensei lesson excerpt.
3383
     *
3384
     * @since 1.9.0
3385
     * @param string $excerpt
3386
     * @return string $excerpt
3387
     */
3388
    public static function alter_the_lesson_excerpt( $excerpt ) {
3389
3390
        if ('lesson' == get_post_type(get_the_ID())){
3391
3392
            // remove this hooks to avoid an infinite loop.
3393
            remove_filter( 'get_the_excerpt', array( 'WooThemes_Sensei_Lesson','alter_the_lesson_excerpt') );
3394
3395
            return WooThemes_Sensei_Lesson::lesson_excerpt( get_post( get_the_ID() ) );
3396
        }
3397
3398
        return $excerpt;
3399
3400
    }// end the_lesson_excerpt
3401
3402
    /**
3403
     * Returns the lesson prerequisite for the given lesson id.
3404
     *
3405
     * @since 1.9.0
3406
     *
3407
     * @param $current_lesson_id
3408
     * @return mixed | bool | int $prerequisite_lesson_id or false
3409
     */
3410
    public static function get_lesson_prerequisite_id( $current_lesson_id  ){
3411
3412
        $prerequisite_lesson_id = get_post_meta( $current_lesson_id , '_lesson_prerequisite', true );
3413
3414
        // set ti to false if not a valid prerequisite lesson id
3415
        if(  empty( $prerequisite_lesson_id )
3416
            || 'lesson' != get_post_type( $prerequisite_lesson_id )
3417
            || $prerequisite_lesson_id == $current_lesson_id  ) {
3418
3419
            $prerequisite_lesson_id = false;
3420
3421
        }
3422
3423
        return apply_filters( 'sensei_lesson_prerequisite', $prerequisite_lesson_id, $current_lesson_id );
3424
3425
    }
3426
3427
    /**
3428
     * This function requires that you pass in the lesson you would like to check for
3429
     * a pre-requisite and not the pre-requisite. It will check if the
3430
     * lesson has a pre-requiste and then check if it is completed.
3431
     *
3432
     * @since 1.9.0
3433
     *
3434
     * @param $lesson_id
3435
     * @param $user_id
3436
     * @return bool
3437
     */
3438
    public  static function is_prerequisite_complete( $lesson_id, $user_id  ){
3439
3440 View Code Duplication
        if( empty( $lesson_id ) || empty( $user_id )
3441
        || 'lesson' != get_post_type( $lesson_id )
3442
        ||  ! is_a( get_user_by( 'id', $user_id ), 'WP_User' )){
3443
3444
            return false;
3445
3446
        }
3447
3448
        $pre_requisite_id = (string) self::get_lesson_prerequisite_id( $lesson_id );
3449
3450
        // not a valid pre-requisite so pre-requisite is completed
3451
        if( 'lesson' != get_post_type( $pre_requisite_id )
3452
            || ! is_numeric( $pre_requisite_id ) ){
3453
3454
            return true;
3455
3456
        }
3457
3458
        return  Sensei_Utils::user_completed_lesson( $pre_requisite_id, $user_id );
3459
3460
    }// end is_prerequisite_complete
3461
3462
    /**
3463
     * Show the user not taking course message if it is the case
3464
     *
3465
     * @since 1.9.0
3466
     */
3467
    public  static function user_not_taking_course_message(){
3468
3469
        $lesson_id = get_the_ID();
3470
3471
        if( 'lesson' != get_post_type( $lesson_id ) ){
3472
            return;
3473
        }
3474
3475
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id );
3476
        $pre_requisite_complete = self::is_prerequisite_complete( $lesson_id , get_current_user_id() );
3477
        $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true );
3478
        $user_taking_course = Sensei_Utils::user_started_course( $lesson_course_id, get_current_user_id() );
3479
3480
        if ( $pre_requisite_complete && $is_preview && !$user_taking_course ) {
3481
3482
3483
        }// end if
3484
3485
    } // end user_not_taking_course_message
3486
3487
    /**
3488
     * Outputs the lessons course signup lingk
3489
     *
3490
     * This hook runs inside the single lesson page.
3491
     *
3492
     * @since 1.9.0
3493
     */
3494
    public static function course_signup_link( ){
3495
3496
        $course_id =  Sensei()->lesson->get_course_id( get_the_ID() );
3497
3498
        if ( empty( $course_id ) || 'course' != get_post_type( $course_id ) || sensei_all_access() ) {
3499
3500
            return;
3501
3502
        }
3503
3504
        ?>
3505
3506
        <section class="course-signup lesson-meta">
3507
3508
            <?php
3509
3510
            global $current_user;
3511
            $wc_post_id = (int) get_post_meta( $course_id, '_course_woocommerce_product', true );
3512
3513
            if ( Sensei_WC::is_woocommerce_active() && Sensei_WC::is_course_purchasable( $course_id ) ) {
3514
3515 View Code Duplication
                if( is_user_logged_in() && ! Sensei_Utils::user_started_course( $course_id, $current_user->ID )  ) {
3516
3517
	                    $a_element = '<a href="' . esc_url( get_permalink( $course_id ) ) . '" title="' . __( 'Sign Up', 'woothemes-sensei' )  . '">';
3518
	                    $a_element .= __( 'course', 'woothemes-sensei' );
3519
	                    $a_element .= '</a>';
3520
3521
	                    if( Sensei_Utils::is_preview_lesson( get_the_ID()  ) ){
3522
3523
		                    $message = sprintf( __( 'This is a preview lesson. Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3524
3525
	                    }else{
3526
3527
		                    $message = sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3528
3529
	                    }
3530
3531
	                    Sensei()->notices->add_notice( $message, 'info' );
3532
3533
                }
3534
3535 View Code Duplication
	            if( ! is_user_logged_in() ) {
3536
3537
	                $a_element = '<a href="' . esc_url( get_permalink( $course_id ) ) . '" title="' . __( 'Sign Up', 'woothemes-sensei' )  . '">';
3538
	                $a_element .= __( 'course', 'woothemes-sensei' );
3539
	                $a_element .= '</a>';
3540
3541
	                if( Sensei_Utils::is_preview_lesson( get_the_ID()  ) ){
3542
3543
						$message = sprintf( __( 'This is a preview lesson. Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3544
3545
					}else{
3546
3547
						$message = sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3548
3549
					}
3550
3551
					Sensei()->notices->add_notice( $message, 'alert' );
3552
3553
	            }
3554
3555
            } else { ?>
3556
3557
	            <?php if( ! Sensei_Utils::user_started_course( $course_id, get_current_user_id() ) &&  sensei_is_login_required() )  : ?>
3558
3559
	                <div class="sensei-message alert">
3560
	                    <?php
3561
	                    $course_link =  '<a href="'
3562
	                                        . esc_url( get_permalink( $course_id ) )
3563
	                                        . '" title="' . __( 'Sign Up', 'woothemes-sensei' )
3564
	                                        . '">' . __( 'course', 'woothemes-sensei' )
3565
	                                    . '</a>';
3566
3567
						if ( Sensei_Utils::is_preview_lesson( get_the_ID( ) ) ) {
3568
3569
							echo sprintf( __( 'This is a preview lesson. Please sign up for the %1$s to access all lessons.', 'woothemes-sensei' ),  $course_link );
3570
3571
						} else {
3572
3573
							echo sprintf( __( 'Please sign up for the %1$s before starting the lesson.', 'woothemes-sensei' ),  $course_link );
3574
3575
						}
3576
3577
	                    ?>
3578
	                </div>
3579
3580
	            <?php endif; ?>
3581
3582
            <?php } // End If Statement ?>
3583
3584
        </section>
3585
3586
        <?php
3587
    }// end course_signup_link
3588
3589
    /**
3590
     * Show a message telling the user to complete the previous message if they haven't done so yet
3591
     *
3592
     * @since 1.9.0
3593
     */
3594
    public  static function prerequisite_complete_message(){
3595
3596
        $lesson_prerequisite =  WooThemes_Sensei_Lesson::get_lesson_prerequisite_id( get_the_ID() );
3597
        $lesson_has_pre_requisite = $lesson_prerequisite > 0;
3598
        if ( ! WooThemes_Sensei_Lesson::is_prerequisite_complete(  get_the_ID(), get_current_user_id() ) && $lesson_has_pre_requisite ) {
3599
3600
            $prerequisite_lesson_link  = '<a href="' . esc_url( get_permalink( $lesson_prerequisite ) ) . '" title="' . esc_attr(  sprintf( __( 'You must first complete: %1$s', 'woothemes-sensei' ), get_the_title( $lesson_prerequisite ) ) ) . '">' . get_the_title( $lesson_prerequisite ). '</a>';
3601
            echo sprintf( __( 'You must first complete %1$s before viewing this Lesson', 'woothemes-sensei' ), $prerequisite_lesson_link );
3602
3603
        }
3604
3605
    }
3606
3607
    /**
3608
     * Deprecate the sensei_lesson_archive_header hook but keep it
3609
     * active for backwards compatibility.
3610
     *
3611
     * @deprecated since 1.9.0
3612
     */
3613
    public static function deprecate_sensei_lesson_archive_header_hook(){
3614
3615
        sensei_do_deprecated_action('sensei_lesson_archive_header', '1.9.0', 'sensei_loop_lesson_inside_before');
3616
3617
    }
3618
3619
    /**
3620
     * Outputs the the lesson archive header.
3621
     *
3622
     * @since  1.9.0
3623
     * @return void
3624
     */
3625
    public function the_archive_header( ) {
3626
3627
        $before_html = '<header class="archive-header"><h1>';
3628
        $after_html = '</h1></header>';
3629
        $html = $before_html .  __( 'Lessons Archive', 'woothemes-sensei' ) . $after_html;
3630
3631
        echo apply_filters( 'sensei_lesson_archive_title', $html );
3632
3633
    } // sensei_course_archive_header()
3634
3635
    /**
3636
     * Output the title for the single lesson page
3637
     *
3638
     * @global $post
3639
     * @since 1.9.0
3640
     */
3641
    public static function the_title(){
3642
3643
        global $post;
3644
3645
        ?>
3646
        <header>
3647
3648
            <h1>
3649
3650
                <?php
3651
                /**
3652
                 * Filter documented in class-sensei-messages.php the_title
3653
                 */
3654
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
3655
                ?>
3656
3657
            </h1>
3658
3659
        </header>
3660
3661
        <?php
3662
3663
    }//the_title
3664
3665
    /**
3666
     * Flush the rewrite rules for a lesson post type
3667
     *
3668
     * @since 1.9.0
3669
     *
3670
     * @param $post_id
3671
     */
3672 View Code Duplication
    public static function flush_rewrite_rules( $post_id ){
3673
3674
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
3675
3676
            return;
3677
3678
        }
3679
3680
3681
        if( 'lesson' == get_post_type( $post_id )  ){
3682
3683
            Sensei()->initiate_rewrite_rules_flush();
3684
3685
        }
3686
3687
    }
3688
3689
    /**
3690
     * Output the quiz specific buttons and messaging on the single lesson page
3691
     *
3692
     *
3693
     * @since 1.0.0 moved here from frontend class
3694
     *
3695
     * @param int $lesson_id
3696
     * @param int $user_id
3697
     */
3698
    public static function footer_quiz_call_to_action( $lesson_id = 0, $user_id = 0 ) {
3699
3700
3701
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3702
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3703
3704
3705
	    if ( ! sensei_can_user_view_lesson( $lesson_id, $user_id ) ) {
3706
		    return;
3707
	    }
3708
3709
        $lesson_prerequisite       = (int) get_post_meta( $lesson_id, '_lesson_prerequisite', true );
3710
        $lesson_course_id          = (int) get_post_meta( $lesson_id, '_lesson_course', true );
3711
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3712
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3713
        $show_actions              = is_user_logged_in() ? true : false;
3714
3715
        if( intval( $lesson_prerequisite ) > 0 ) {
3716
3717
            // If the user hasn't completed the prereq then hide the current actions
3718
            $show_actions = Sensei_Utils::user_completed_lesson( $lesson_prerequisite, $user_id );
3719
3720
        }
3721
3722
        ?>
3723
3724
        <footer>
3725
3726
            <?php
3727
            if( $show_actions && $quiz_id && Sensei()->access_settings() ) {
3728
3729
                $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3730
                if( $has_quiz_questions ) {
3731
                    ?>
3732
3733
                    <p>
3734
3735
                        <a class="button"
3736
                           href="<?php echo esc_url_raw( get_permalink( $quiz_id ) ); ?>"
3737
                           title="<?php _e( 'View the Lesson Quiz', 'woothemes-sensei'  ); ?>">
3738
3739
                            <?php  _e( 'View the Lesson Quiz', 'woothemes-sensei' ); ?>
3740
3741
                        </a>
3742
3743
                    </p>
3744
3745
                    <?php
3746
                }
3747
3748
            } // End If Statement
3749
3750
            if ( $show_actions && ! $has_user_completed_lesson ) {
3751
3752
                sensei_complete_lesson_button();
3753
3754
            } elseif( $show_actions ) {
3755
3756
                sensei_reset_lesson_button();
3757
3758
            } // End If Statement
3759
            ?>
3760
3761
        </footer>
3762
3763
        <?php
3764
    } // End sensei_lesson_quiz_meta()
3765
3766
    /**
3767
     * Show the lesson comments. This should be used in the loop.
3768
     *
3769
     * @since 1.9.0
3770
     */
3771
    public static function output_comments(){
3772
3773
        if( ! is_user_logged_in() ){
3774
            return;
3775
        }
3776
3777
        $pre_requisite_complete = Sensei()->lesson->is_prerequisite_complete( get_the_ID(), get_current_user_id() );
3778
        $course_id = Sensei()->lesson->get_course_id( get_the_ID() );
3779
        $allow_comments = Sensei()->settings->settings[ 'lesson_comments' ];
3780
        $user_taking_course = Sensei_Utils::user_started_course($course_id );
3781
3782
        $lesson_allow_comments = $allow_comments && $pre_requisite_complete  && $user_taking_course;
3783
3784
        if (  $lesson_allow_comments || is_singular( 'sensei_message' ) ) {
3785
3786
            comments_template();
3787
3788
        } // End If Statement
3789
3790
    } //output_comments
3791
3792
    /**
3793
     * Display the leeson quiz status if it should be shown
3794
     *
3795
     * @param int $lesson_id defaults to the global lesson id
3796
     * @param int $user_id defaults to the current user id
3797
     *
3798
     * @since 1.9.0
3799
     */
3800
    public static function user_lesson_quiz_status_message( $lesson_id = 0, $user_id = 0){
3801
3802
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3803
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3804
        $lesson_course_id          = (int) get_post_meta( $lesson_id, '_lesson_course', true );
3805
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3806
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3807
3808
3809
        if ( $quiz_id && is_user_logged_in()
3810
            && Sensei_Utils::user_started_course( $lesson_course_id, $user_id ) ) {
3811
3812
            $no_quiz_count = 0;
3813
            $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3814
3815
            // Display lesson quiz status message
3816
            if ( $has_user_completed_lesson || $has_quiz_questions ) {
3817
                $status = Sensei_Utils::sensei_user_quiz_status_message( $lesson_id, $user_id, true );
3818
                echo '<div class="sensei-message ' . $status['box_class'] . '">' . $status['message'] . '</div>';
3819
                if( $has_quiz_questions ) {
3820
                   // echo $status['extra'];
3821
                } // End If Statement
3822
            } // End If Statement
3823
3824
        }
3825
3826
    }
3827
3828
    /**
3829
     * On the lesson archive limit the number of words the show up if the access settings are enabled
3830
     *
3831
     * @since 1.9.0
3832
     * @param $content
3833
     * @return string
3834
     */
3835
    public static function limit_archive_content ( $content ){
3836
3837
        if( is_archive('lesson') && Sensei()->settings->get('access_permission') ){
3838
3839
            return wp_trim_words( $content, $num_words = 30, $more = '…' );
3840
        }
3841
3842
        return $content;
3843
3844
    } // end limit_archive_content
3845
3846
    /**
3847
     * Returns all publised lesson ID's
3848
     *
3849
     * @since 1.9.0
3850
     * @return array
3851
     */
3852
    public static function get_all_lesson_ids(){
3853
3854
        return get_posts( array(
3855
            'post_type'=>'lesson',
3856
            'fields'=>'ids',
3857
            'post_status' => 'publish',
3858
            'numberposts' => 4000, // legacy support
3859
            'post_per_page' => 4000
3860
        ));
3861
3862
    }
3863
3864
} // End Class
3865
3866
/**
3867
 * Class WooThemes_Sensei_Lesson
3868
 * @ignore only for backward compatibility
3869
 * @since 1.9.0
3870
 */
3871
class WooThemes_Sensei_Lesson extends Sensei_Lesson{}
3872