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

Sensei_Lesson::quiz_update()   D

Complexity

Conditions 20
Paths 111

Size

Total Lines 109
Code Lines 55

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 109
rs 4.5687
cc 20
eloc 55
nc 111
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
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 = '';
0 ignored issues
show
Bug introduced by
The property question_order does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
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
Unused Code introduced by
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
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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
		// Check if the current post type is a page
259 View Code Duplication
		if ( 'page' == $_POST[ 'post_type' ] ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
260
			if ( ! current_user_can( 'edit_page', $post_id ) ) {
261
				return $post_id;
262
			} // End If Statement
263
		} else {
264
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
265
				return $post_id;
266
			} // End If Statement
267
		} // End If Statement
268
		// Save the post meta data fields
269 View Code Duplication
		if ( isset($this->meta_fields) && is_array($this->meta_fields) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
270
			foreach ( $this->meta_fields as $meta_key ) {
271
				$this->save_post_meta( $meta_key, $post_id );
272
			} // End For Loop
273
		} // End If Statement
274
	} // End meta_box_save()
275
276
277
	/**
278
     * Update the lesson quiz and all the post meta
279
	 *
280
	 * @access public
281
	 * @return integer|boolean $post_id or false
282
	 */
283
	public function quiz_update( $post_id ) {
284
		global $post;
285
		// Verify the nonce before proceeding.
286
		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') ) {
287
			if ( isset($post->ID) ) {
288
				return $post->ID;
289
			} else {
290
				return false;
291
			} // End If Statement
292
		} // End If Statement
293
294
		if( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
295
			return false;
296
		}
297
298
		// Temporarily disable the filter
299
        remove_action( 'save_post', array( $this, 'quiz_update' ) );
300
		// Save the Quiz
301
		$quiz_id = $this->lesson_quizzes( $post_id, 'any');
302
303
		 // Sanitize and setup the post data
304
		$_POST = stripslashes_deep( $_POST );
305
		if ( isset( $_POST[ 'quiz_id' ] ) && ( 0 < absint( $_POST[ 'quiz_id' ] ) ) ) {
306
			$quiz_id = absint( $_POST[ 'quiz_id' ] );
307
		} // End If Statement
308
		$post_title = esc_html( $_POST[ 'post_title' ] );
309
		$post_status = esc_html( $_POST[ 'post_status' ] );
310
		$post_content = '';
311
312
		// Setup Query Arguments
313
		$post_type_args = array(	'post_content' => $post_content,
314
  		    						'post_status' => $post_status,
315
  		    						'post_title' => $post_title,
316
  		    						'post_type' => 'quiz',
317
                                    'post_parent' => $post_id,
318
  		    						);
319
320
		$settings = $this->get_quiz_settings();
321
322
  		// Update or Insert the Lesson Quiz
323
		if ( 0 < $quiz_id ) {
324
			// Update the Quiz
325
			$post_type_args[ 'ID' ] = $quiz_id;
326
		    wp_update_post($post_type_args);
327
328
		    // Update the post meta data
329
		    update_post_meta( $quiz_id, '_quiz_lesson', $post_id );
330
331
		    foreach( $settings as $field ) {
332
		    	if( 'random_question_order' != $field['id'] ) {
333
			    	$value = $this->get_submitted_setting_value( $field );
334
			    	if( isset( $value ) ) {
335
			    		update_post_meta( $quiz_id, '_' . $field['id'], $value );
336
			    	}
337
			    }
338
		    }
339
340
		    // Set the post terms for quiz-type
341
		    wp_set_post_terms( $quiz_id, array( 'multiple-choice' ), 'quiz-type' );
342
		} else {
343
			// Create the Quiz
344
		    $quiz_id = wp_insert_post($post_type_args);
345
346
		    // Add the post meta data WP will add it if it doesn't exist
347
            update_post_meta( $quiz_id, '_quiz_lesson', $post_id );
348
349
		    foreach( $settings as $field ) {
350
		    	if( 'random_question_order' != $field['id'] ) {
351
352
                    //ignore values not posted to avoid
353
                    // overwriting with empty or default values
354
                    // when the values are posted from bulk edit or quick edit
355
                    if( !isset( $_POST[ $field['id'] ] ) ){
356
                        continue;
357
                    }
358
359
			    	$value = $this->get_submitted_setting_value( $field );
360
			    	if( isset( $value ) ) {
361
			    		add_post_meta( $quiz_id, '_' . $field['id'], $value );
362
			    	}
363
			    }
364
		    }
365
366
		    // Set the post terms for quiz-type
367
		    wp_set_post_terms( $quiz_id, array( 'multiple-choice' ), 'quiz-type' );
368
		} // End If Statement
369
370
		// Add default lesson order meta value
371
		$course_id = get_post_meta( $post_id, '_lesson_course', true );
372
		if( $course_id ) {
373
			if( ! get_post_meta( $post_id, '_order_' . $course_id, true ) ) {
374
				update_post_meta( $post_id, '_order_' . $course_id, 0 );
375
			}
376
		}
377
		// Add reference back to the Quiz
378
		update_post_meta( $post_id, '_lesson_quiz', $quiz_id );
379
		// Mark if the Lesson Quiz has questions
380
		$quiz_questions = Sensei()->lesson->lesson_quiz_questions( $quiz_id );
381
		if( 0 < count( $quiz_questions ) ) {
382
			update_post_meta( $post_id, '_quiz_has_questions', '1' );
383
		}
384
		else {
385
			delete_post_meta( $post_id, '_quiz_has_questions' );
386
		}
387
388
		// Restore the previously disabled filter
389
        add_action( 'save_post', array( $this, 'quiz_update' ) );
390
391
	} // End post_updated()
392
393
	public function get_submitted_setting_value( $field = false ) {
394
395
		if( ! $field ) return;
396
397
		$value = false;
0 ignored issues
show
Unused Code introduced by
$value 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...
398
399
		if( 'quiz_grade_type' == $field['id'] ) {
400
			if( isset( $_POST[ $field['id'] ] ) && 'on' == $_POST[ $field['id'] ] ) {
401
				$value = 'auto';
402
			} else {
403
				$value = 'manual';
404
			}
405
			return $value;
406
		}
407
408
		if ( isset( $_POST[ $field['id'] ] ) ) {
409
			$value = $_POST[ $field['id'] ];
410
		} else {
411
			$value = $field['default'];
412
		}
413
414
		return $value;
415
	}
416
417
	/**
418
	 * save_post_meta function.
419
	 * Saves lesson meta data
420
	 * @access private
421
	 * @param string $post_key (default: '')
422
	 * @param int $post_id (default: 0)
423
	 * @return int|bool meta id or saved status
424
	 */
425
	private function save_post_meta( $post_key = '', $post_id = 0 ) {
426
		// Get the meta key.
427
		$meta_key = '_' . $post_key;
428
429
        //ignore fields are not posted
430
431
        if( !isset( $_POST[ $post_key ] ) ){
432
433
            // except for lesson preview checkbox field
434
            if( 'lesson_preview' == $post_key ){
435
436
                $_POST[ $post_key ] = '';
437
438
            } else {
439
440
                return false;
441
442
            }
443
444
        }
445
446
		// Get the posted data and sanitize it for use as an HTML class.
447 View Code Duplication
		if ( 'lesson_video_embed' == $post_key) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
448
			$new_meta_value = esc_html( $_POST[$post_key] );
449
		} else {
450
			$new_meta_value = ( isset( $_POST[$post_key] ) ? sanitize_html_class( $_POST[$post_key] ) : '' );
451
		} // End If Statement
452
453
        // update field with the new value
454
        if( -1 != $new_meta_value  ){
455
            return update_post_meta( $post_id, $meta_key, $new_meta_value );
456
        }
457
458
	} // End save_post_meta()
459
460
	/**
461
	 * lesson_course_meta_box_content function.
462
	 *
463
	 * @access public
464
	 * @return void
465
	 */
466
	public function lesson_course_meta_box_content () {
467
		global $post;
468
		// Setup Lesson Meta Data
469
		$selected_lesson_course = 0;
470
		if ( 0 < $post->ID ) {
471
			$selected_lesson_course = get_post_meta( $post->ID, '_lesson_course', true );
472
		} // End If Statement
473
		// Handle preselected course
474 View Code Duplication
		if ( isset( $_GET[ 'course_id' ] ) && ( 0 < absint( $_GET[ 'course_id' ] ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
475
			$selected_lesson_course = absint( $_GET[ 'course_id' ] );
476
		} // End If Statement
477
		// Get the Lesson Posts
478
		$post_args = array(	'post_type' 		=> 'course',
479
							'posts_per_page' 		=> -1,
480
							'orderby'         	=> 'title',
481
    						'order'           	=> 'ASC',
482
    						'post_status'      	=> 'any',
483
    						'suppress_filters' 	=> 0,
484
							);
485
		$posts_array = get_posts( $post_args );
486
		// Buid the HTML to Output
487
		$html = '';
488
		// Nonce
489
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
490
491
        // Select the course for the lesson
492
        $drop_down_args = array(
493
            'name'=>'lesson_course',
494
            'id' => 'lesson-course-options'
495
        );
496
497
        $courses = WooThemes_Sensei_Course::get_all_courses();
498
        $courses_options = array();
499
        foreach( $courses as $course ){
500
            $courses_options[ $course->ID ] = get_the_title( $course ) ;
501
        }
502
        $html .= Sensei_Utils::generate_drop_down( $selected_lesson_course, $courses_options, $drop_down_args );
503
504
        // Course Actions Panel
505
		if ( current_user_can( 'publish_courses' )) {
506
				$html .= '<div id="lesson-course-actions">';
507
					$html .= '<p>';
508
						// Add a course action link
509
						$html .= '<a id="lesson-course-add" href="#course-add" class="lesson-add-course">+ ' . __('Add New Course', 'woothemes-sensei' ) . '</a>';
510
					$html .= '</p>';
511
				$html .= '</div>';
512
				// Add a course input fields
513
				$html .= '<div id="lesson-course-details" class="hidden">';
514
					$html .= '<p>';
515
						// Course Title input
516
						$html .= '<label>' . __( 'Course Title' , 'woothemes-sensei' ) . '</label> ';
517
	  					$html .= '<input type="text" id="course-title" name="course_title" value="" size="25" class="widefat" />';
518
	  					// Course Description input
519
	  					$html .= '<label>' . __( 'Description' , 'woothemes-sensei' ) . '</label> ';
520
	  					$html .= '<textarea rows="10" cols="40" id="course-content" name="course_content" value="" size="300" class="widefat"></textarea>';
521
	  					// Course Prerequisite
522
	  					$html .= '<label>' . __( 'Course Prerequisite' , 'woothemes-sensei' ) . '</label> ';
523
	  					$html .= '<select id="course-prerequisite-options" name="course_prerequisite" class="chosen_select widefat">' . "\n";
524
							$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
525
							foreach ($posts_array as $post_item){
526
								$html .= '<option value="' . esc_attr( absint( $post_item->ID ) ) . '">' . esc_html( $post_item->post_title ) . '</option>' . "\n";
527
							} // End For Loop
528
						$html .= '</select>' . "\n";
529
						// Course Product
530
                        if ( Sensei_WC::is_woocommerce_active() ) {
531
	  						// Get the Products
532
							$select_course_woocommerce_product = get_post_meta( $post_item->ID, '_course_woocommerce_product', true );
0 ignored issues
show
Unused Code introduced by
$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...
Bug introduced by
The variable $post_item seems to be defined by a foreach iteration on line 525. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
533
534
							$product_args = array(	'post_type' 		=> array( 'product', 'product_variation' ),
535
													'posts_per_page' 		=> -1,
536
													'orderby'         	=> 'title',
537
	    											'order'           	=> 'DESC',
538
	    											'post_status'		=> array( 'publish', 'private', 'draft' ),
539
	    											'tax_query'			=> array(
540
														array(
541
															'taxonomy'	=> 'product_type',
542
															'field'		=> 'slug',
543
															'terms'		=> array( 'variable', 'grouped' ),
544
															'operator'	=> 'NOT IN'
545
														)
546
													),
547
	    											'suppress_filters' 	=> 0
548
													);
549
							$products_array = get_posts( $product_args );
550
							$html .= '<label>' . __( 'WooCommerce Product' , 'woothemes-sensei' ) . '</label> ';
551
	  						$html .= '<select id="course-woocommerce-product-options" name="course_woocommerce_product" class="chosen_select widefat">' . "\n";
552
								$html .= '<option value="-">' . __( 'None', 'woothemes-sensei' ) . '</option>';
553
								$prev_parent_id = 0;
554
								foreach ($products_array as $products_item){
555
556
									if ( 'product_variation' == $products_item->post_type ) {
557
										$product_object = get_product( $products_item->ID );
558
										$parent_id = wp_get_post_parent_id( $products_item->ID );
559
										$product_name = ucwords( woocommerce_get_formatted_variation( $product_object->variation_data, true ) );
0 ignored issues
show
Unused Code introduced by
$product_name 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...
560
									} else {
561
										$parent_id = false;
562
										$prev_parent_id = 0;
563
										$product_name = $products_item->post_title;
0 ignored issues
show
Unused Code introduced by
$product_name 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...
564
									}
565
566
									// Show variations in groups
567 View Code Duplication
									if( $parent_id && $parent_id != $prev_parent_id ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
568
										if( 0 != $prev_parent_id ) {
569
											$html .= '</optgroup>';
570
										}
571
										$html .= '<optgroup label="' . get_the_title( $parent_id ) . '">';
572
										$prev_parent_id = $parent_id;
573
									} elseif( ! $parent_id && 0 == $prev_parent_id ) {
574
										$html .= '</optgroup>';
575
									}
576
577
									$html .= '<option value="' . esc_attr( absint( $products_item->ID ) ) . '">' . esc_html( $products_item->post_title ) . '</option>' . "\n";
578
								} // End For Loop
579
							$html .= '</select>' . "\n";
580
						} else {
581
							// Default
582
							$html .= '<input type="hidden" name="course_woocommerce_product" id="course-woocommerce-product-options" value="-" />';
583
						}
584
						// Course Category
585
	  					$html .= '<label>' . __( 'Course Category' , 'woothemes-sensei' ) . '</label> ';
586
	  					$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' );
587
						$html .= wp_dropdown_categories(apply_filters('widget_course_categories_dropdown_args', $cat_args)) . "\n";
588
	  					// Save the course action button
589
	  					$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>';
590
						$html .= '&nbsp;&nbsp;&nbsp;';
591
						// Cancel action link
592
						$html .= '<a href="#course-add-cancel" class="lesson_course_cancel">' . __( 'Cancel', 'woothemes-sensei' ) . '</a>';
593
					$html .= '</p>';
594
				$html .= '</div>';
595
			} // End If Statement
596
597
		// Output the HTML
598
		echo $html;
599
	} // End lesson_course_meta_box_content()
600
601
	public function quiz_panel( $quiz_id = 0 ) {
602
603
		$html = wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
604
		$html .= '<div id="add-quiz-main">';
605 View Code Duplication
			if ( 0 == $quiz_id ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
606
				$html .= '<p>';
607
					// Default message and Add a Quiz button
608
					$html .= esc_html( __( 'Once you have saved your lesson you will be able to add questions.', 'woothemes-sensei' ) );
609
				$html .= '</p>';
610
			}
611
612
			// Quiz Panel CSS Class
613
			$quiz_class = '';
614
			if ( 0 == $quiz_id ) {
615
				$quiz_class = ' class="hidden"';
616
			} // End If Statement
617
			// Build the HTML to Output
618
			$message_class = '';
0 ignored issues
show
Unused Code introduced by
$message_class 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...
619
620
			// Setup Questions Query
621
			$questions = array();
622
			if ( 0 < $quiz_id ) {
623
				$questions = $this->lesson_quiz_questions( $quiz_id );
624
			} // End If Statement
625
626
			$question_count = 0;
627 View Code Duplication
			foreach( $questions as $question ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
628
629
				if( $question->post_type == 'multiple_question' ) {
630
					$question_number = get_post_meta( $question->ID, 'number', true );
631
					$question_count += $question_number;
632
				} else {
633
					++$question_count;
634
				}
635
636
			}
637
638
			// Inner DIV
639
			$html .= '<div id="add-quiz-metadata"' . $quiz_class . '>';
640
641
				// Quiz ID
642
				$html .= '<input type="hidden" name="quiz_id" id="quiz_id" value="' . esc_attr( $quiz_id ) . '" />';
643
644
				// Default Message
645 View Code Duplication
				if ( 0 == $quiz_id ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
646
					$html .= '<p class="save-note">';
647
						$html .= esc_html( __( 'Please save your lesson in order to add questions to your quiz.', 'woothemes-sensei' ) );
648
					$html .= '</p>';
649
				} // End If Statement
650
651
			$html .= '</div>';
652
653
			// Question Container DIV
654
			$html .= '<div id="add-question-main"' . $quiz_class . '>';
655
				// Inner DIV
656
				$html .= '<div id="add-question-metadata">';
657
658
					// Count of questions
659
					$html .= '<input type="hidden" name="question_counter" id="question_counter" value="' . esc_attr( $question_count ) . '" />';
660
					// Table headers
661
					$html .= '<table class="widefat" id="sortable-questions">
662
								<thead>
663
								    <tr>
664
								        <th class="question-count-column">#</th>
665
								        <th>' . __( 'Question', 'woothemes-sensei' ) . '</th>
666
								        <th style="width:45px;">' . __( 'Grade', 'woothemes-sensei' ) . '</th>
667
								        <th style="width:125px;">' . __( 'Type', 'woothemes-sensei' ) . '</th>
668
								        <th style="width:125px;">' . __( 'Action', 'woothemes-sensei' ) . '</th>
669
								    </tr>
670
								</thead>
671
								<tfoot>
672
								    <tr>
673
									    <th class="question-count-column">#</th>
674
									    <th>' . __( 'Question', 'woothemes-sensei' ) . '</th>
675
									    <th>' . __( 'Grade', 'woothemes-sensei' ) . '</th>
676
									    <th>' . __( 'Type', 'woothemes-sensei' ) . '</th>
677
									    <th>' . __( 'Action', 'woothemes-sensei' ) . '</th>
678
								    </tr>
679
								</tfoot>';
680
681
					$message_class = '';
682
					if ( 0 < $question_count ) { $message_class = 'hidden'; }
683
684
					$html .= '<tbody id="no-questions-message" class="' . esc_attr( $message_class ) . '">';
685
						$html .= '<tr>';
686
							$html .= '<td colspan="5">' . __( 'There are no Questions for this Quiz yet. Please add some below.', 'woothemes-sensei' ) . '</td>';
687
						$html .= '</tr>';
688
					$html .= '</tbody>';
689
690
					if( 0 < $question_count ) {
691
						$html .= $this->quiz_panel_questions( $questions );
692
					}
693
694
					$html .= '</table>';
695
696
					if( ! isset( $this->question_order ) ) {
697
						$this->question_order = '';
698
					}
699
700
					$html .= '<input type="hidden" id="question-order" name="question-order" value="' . $this->question_order . '" />';
701
702
				$html .= '</div>';
703
704
				// Question Action Container DIV
705
				$html .= '<div id="add-question-actions">';
706
707
					$html .= $this->quiz_panel_add();
708
709
				$html .= '</div>';
710
711
			$html .= '</div>';
712
713
		$html .= '</div>';
714
715
		return $html;
716
717
	}
718
719
	public function quiz_panel_questions( $questions = array() ) {
720
		global $quiz_questions;
721
722
		$quiz_questions = $questions;
723
724
		$html = '';
725
726
		if( count( $questions ) > 0 ) {
727
728
			$question_class = '';
0 ignored issues
show
Unused Code introduced by
$question_class 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...
729
			$question_counter = 1;
730
731
			foreach ( $questions as $question ) {
732
733
				$question_id = $question->ID;
734
735
				$question_type = Sensei()->question->get_question_type( $question_id );
736
737
				$multiple_data = array();
738
				$question_increment = 1;
739
				if( 'multiple_question' == $question->post_type ) {
740
					$question_type = 'category';
741
742
					$question_category = get_post_meta( $question->ID, 'category', true );
743
					$question_cat = get_term( $question_category, 'question-category' );
744
745
					$question_number = get_post_meta( $question->ID, 'number', true );
746
					$question_increment = $question_number;
747
748
					$multiple_data = array( $question_cat->name, $question_number );
749
				}
750
751
				if( ! $question_type ) {
752
					$question_type = 'multiple-choice';
753
				}
754
755
				// Row with question and actions
756
				$html .= $this->quiz_panel_question( $question_type, $question_counter, $question_id, 'quiz', $multiple_data );
757
				$question_counter += $question_increment;
758
759
				if( isset( $this->question_order ) && strlen( $this->question_order ) > 0 ) { $this->question_order .= ','; }
760
				$this->question_order .= $question_id;
761
			} // End For Loop
762
		}
763
764
		return $html;
765
766
	}
767
768
	public function quiz_panel_question( $question_type = '', $question_counter = 0, $question_id = 0, $context = 'quiz', $multiple_data = array() ) {
769
		global $row_counter,  $quiz_questions;
770
771
		$html = '';
772
773
		$question_class = '';
774
		if( 'quiz' == $context ) {
775
			if( ! $row_counter || ! isset( $row_counter ) ) {
776
				$row_counter = 1;
777
			}
778
			if( $row_counter % 2 ) { $question_class = 'alternate'; }
779
			++$row_counter;
780
		}
781
782
		if( $question_id ) {
783
784
			if( $question_type != 'category' ) {
785
786
				$question_grade = Sensei()->question->get_question_grade( $question_id );
787
788
				$question_media = get_post_meta( $question_id, '_question_media', true );
789
				$question_media_type = $question_media_thumb = $question_media_link = $question_media_title = '';
0 ignored issues
show
Unused Code introduced by
$question_media_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...
Unused Code introduced by
$question_media_type 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...
790
				$question_media_thumb_class = $question_media_link_class = $question_media_delete_class = 'hidden';
791
				$question_media_add_button = __( 'Add file', 'woothemes-sensei' );
792
				if( 0 < intval( $question_media ) ) {
793
					$mimetype = get_post_mime_type( $question_media );
794
					if( $mimetype ) {
795
						$mimetype_array = explode( '/', $mimetype);
796
						if( isset( $mimetype_array[0] ) && $mimetype_array[0] ) {
797
							$question_media_delete_class = '';
798
							$question_media_type = $mimetype_array[0];
799
							if( 'image' == $question_media_type ) {
800
								$question_media_thumb = wp_get_attachment_thumb_url( $question_media );
801
								if( $question_media_thumb ) {
802
									$question_media_thumb_class = '';
803
								}
804
							}
805
							$question_media_url = wp_get_attachment_url( $question_media );
806
							if( $question_media_url ) {
807
								$attachment = get_post( $question_media );
808
								$question_media_title = $attachment->post_title;
809
810
								if( ! $question_media_title ) {
811
									$question_media_filename = basename( $question_media_url );
812
									$question_media_title = $question_media_filename;
813
								}
814
								$question_media_link = '<a class="' . $question_media_type . '" href="' . esc_url( $question_media_url ) . '" target="_blank">' . $question_media_title . '</a>';
815
								$question_media_link_class = '';
816
							}
817
818
							$question_media_add_button = __( 'Change file', 'woothemes-sensei' );
819
						}
820
					}
821
				}
822
823
				$random_order = get_post_meta( $question_id, '_random_order', true );
824
				if( ! $random_order ) {
825
					$random_order = 'yes';
826
				}
827
828
				if( ! $question_type ) { $question_type = 'multiple-choice'; }
829
			}
830
831
			$html .= '<tbody class="' . $question_class . '">';
832
833
				if( 'quiz' == $context ) {
834
					$html .= '<tr>';
835
						if( $question_type != 'category' ) {
836
							$question = get_post( $question_id );
837
							$html .= '<td class="table-count question-number question-count-column"><span class="number">' . $question_counter . '</span></td>';
838
							$html .= '<td>' . esc_html( $question->post_title ) . '</td>';
839
							$html .= '<td class="question-grade-column">' . esc_html( $question_grade ) . '</td>';
0 ignored issues
show
Bug introduced by
The variable $question_grade does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
840
							$question_types_filtered = ucwords( str_replace( array( '-', 'boolean' ), array( ' ', __( 'True/False', 'woothemes-sensei' ) ), $question_type ) );
841
							$html .= '<td>' . esc_html( $question_types_filtered ) . '</td>';
842
							$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>';
843
844
						} else {
845
846
							$end_number = intval( $question_counter ) + intval( $multiple_data[1] ) - 1;
847
							if( $question_counter == $end_number ) {
848
								$row_numbers = $question_counter;
849
							} else {
850
								$row_numbers = $question_counter . ' - ' . $end_number;
851
							}
852
							$row_title = sprintf( __( 'Selected from \'%1$s\' ', 'woothemes-sensei' ), $multiple_data[0] );
853
854
							$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>';
855
							$html .= '<td>' . esc_html( $row_title ) . '</td>';
856
							$html .= '<td class="question-grade-column"></td>';
857
							$html .= '<td><input type="hidden" name="question_id" class="row_question_id" id="question_' . $question_counter . '_id" value="' . $question_id . '" /></td>';
858
							$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>';
859
860
						}
861
					$html .= '</tr>';
862
				}
863
864
				if( $question_type != 'category' ) {
865
866
					$edit_class = '';
867
					if( 'quiz' == $context ) {
868
						$edit_class = 'hidden';
869
					}
870
871
					$question = get_post( $question_id );
872
					$html .= '<tr class="question-quick-edit ' . esc_attr( $edit_class ) . '">';
873
						$html .= '<td colspan="5">';
874
							$html .= '<span class="hidden question_original_counter">' . $question_counter . '</span>';
875
					    	$html .= '<div class="question_required_fields">';
876
877
						    	// Question title
878
						    	$html .= '<div>';
879
							    	$html .= '<label for="question_' . $question_counter . '">' . __( 'Question:', 'woothemes-sensei' ) . '</label> ';
880
							    	$html .= '<input type="text" id="question_' . $question_counter . '" name="question" value="' . esc_attr( htmlspecialchars( $question->post_title ) ) . '" size="25" class="widefat" />';
881
						    	$html .= '</div>';
882
883
						    	// Question description
884
						    	$html .= '<div>';
885
							    	$html .= '<label for="question_' . $question_counter . '_desc">' . __( 'Question Description (optional):', 'woothemes-sensei' ) . '</label> ';
886
						    	$html .= '</div>';
887
							    	$html .= '<textarea id="question_' . $question_counter . '_desc" name="question_description" class="widefat" rows="4">' . esc_textarea( $question->post_content ) . '</textarea>';
888
889
						    	// Question grade
890
						    	$html .= '<div>';
891
							    	$html .= '<label for="question_' . $question_counter . '_grade">' . __( 'Question grade:', 'woothemes-sensei' ) . '</label> ';
892
							    	$html .= '<input type="number" id="question_' . $question_counter . '_grade" class="question_grade small-text" name="question_grade" min="0" value="' . $question_grade . '" />';
893
						    	$html .= '</div>';
894
895
						    	// Random order
896
						    	if( $question_type == 'multiple-choice' ) {
897
						    		$html .= '<div>';
898
						    			$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>';
0 ignored issues
show
Bug introduced by
The variable $random_order does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
899
						    		$html .= '</div>';
900
						    	}
901
902
						    	// Question media
903
						    	$html .= '<div>';
904
							    	$html .= '<label for="question_' . $question_counter . '_media_button">' . __( 'Question media:', 'woothemes-sensei' ) . '</label><br/>';
905
							    	$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>';
0 ignored issues
show
Bug introduced by
The variable $question_media_add_button does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
906
							    	$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/>';
0 ignored issues
show
Bug introduced by
The variable $question_media_delete_class does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
907
							    	$html .= '<span id="question_' . $question_counter . '_media_link" class="question_media_link ' . $question_media_link_class . '">' . $question_media_link . '</span>';
0 ignored issues
show
Bug introduced by
The variable $question_media_link_class does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $question_media_link does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
908
							    	$html .= '<br/><img id="question_' . $question_counter . '_media_preview" class="question_media_preview ' . $question_media_thumb_class . '" src="' . $question_media_thumb . '" /><br/>';
0 ignored issues
show
Bug introduced by
The variable $question_media_thumb_class does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $question_media_thumb does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
909
							    	$html .= '<input type="hidden" id="question_' . $question_counter . '_media" class="question_media" name="question_media" value="' . $question_media . '" />';
0 ignored issues
show
Bug introduced by
The variable $question_media does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
910
						    	$html .= '</div>';
911
912
						    $html .= '</div>';
913
914
						    $html .= $this->quiz_panel_question_field( $question_type, $question_id, $question_counter );
915
916
						    $html .= '<input type="hidden" id="question_' . $question_counter . '_question_type" class="question_type" name="question_type" value="' . $question_type . '" />';
917
							$html .= '<input type="hidden" name="question_id" class="row_question_id" id="question_' . $question_counter . '_id" value="' . $question_id . '" />';
918
919
							if( 'quiz' == $context ) {
920
					    		$html .= '<div class="update-question">';
921
						    		$html .= '<a href="#question-edit-cancel" class="lesson_question_cancel" title="' . esc_attr( __( 'Cancel', 'woothemes-sensei' ) ) . '">' . __( 'Cancel', 'woothemes-sensei' ) . '</a> ';
922
						    		$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>';
923
					    		$html .= '</div>';
924
					    	}
925
926
			    		$html .= '</td>';
927
					$html .= '</tr>';
928
				}
929
930
			$html .= '</tbody>';
931
932
		}
933
934
		return $html;
935
	}
936
937
	public function quiz_panel_add( $context = 'quiz' ) {
938
939
940
		$html = '<div id="add-new-question">';
941
942
			$question_types = Sensei()->question->question_types();
943
944
			$question_cats = get_terms( 'question-category', array( 'hide_empty' => false ) );
945
946
			if( 'quiz' == $context ) {
947
	    		$html .= '<h2 class="nav-tab-wrapper add-question-tabs">';
948
	    			$html .= '<a id="tab-new" class="nav-tab nav-tab-active">' . __( 'New Question'  , 'woothemes-sensei' ) . '</a>';
949
	    			$html .= '<a id="tab-existing" class="nav-tab">' . __( 'Existing Questions'  , 'woothemes-sensei' ) . '</a>';
950
                    if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats )  && ! Sensei()->teacher->is_admin_teacher() ) {
951
	    				$html .= '<a id="tab-multiple" class="nav-tab">' . __( 'Category Questions'  , 'woothemes-sensei' ) . '</a>';
952
	    			}
953
	    		$html .= '</h2>';
954
	    	}
955
956
	    	$html .= '<div class="tab-content" id="tab-new-content">';
957
958
	    		if( 'quiz' == $context ) {
959
	    			$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>';
960
	    		}
961
962
				$html .= '<div class="question">';
963
					$html .= '<div class="question_required_fields">';
964
965
						// Question title
966
						$html .= '<p><label>' . __( 'Question:'  , 'woothemes-sensei' ) . '</label> ';
967
	  					$html .= '<input type="text" id="add_question" name="question" value="" size="25" class="widefat" /></p>';
968
969
						// Question description
970
						$html .= '<p>';
971
							$html .= '<label for="question_desc">' . __( 'Question Description (optional):', 'woothemes-sensei' ) . '</label> ';
972
						$html .= '</p>';
973
						$html .= '<textarea id="question_desc" name="question_description" class="widefat" rows="4"></textarea>';
974
975
	  					// Question type
976
						$html .= '<p><label>' . __( 'Question Type:' , 'woothemes-sensei' ) . '</label> ';
977
						$html .= '<select id="add-question-type-options" name="question_type" class="chosen_select widefat question-type-select">' . "\n";
978 View Code Duplication
							foreach ( $question_types as $type => $label ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
979
								$html .= '<option value="' . esc_attr( $type ) . '">' . esc_html( $label ) . '</option>' . "\n";
980
							} // End For Loop
981
						$html .= '</select></p>' . "\n";
982
983
						// Question category
984
						if( 'quiz' == $context ) {
985
							if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats ) ) {
986
								$html .= '<p><label>' . __( 'Question Category:' , 'woothemes-sensei' ) . '</label> ';
987
								$html .= '<select id="add-question-category-options" name="question_category" class="chosen_select widefat question-category-select">' . "\n";
988
								$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>' . "\n";
989 View Code Duplication
								foreach( $question_cats as $cat ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
990
									$html .= '<option value="' . esc_attr( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
991
								} // End For Loop
992
								$html .= '</select></p>' . "\n";
993
							}
994
						}
995
996
	  					// Question grade
997
						$html .= '<p><label>' . __( 'Question Grade:'  , 'woothemes-sensei' ) . '</label> ';
998
						$html .= '<input type="number" id="add-question-grade" name="question_grade" class="small-text" min="0" value="1" /></p>' . "\n";
999
1000
						// Random order
1001
						$html .= '<p class="add_question_random_order">';
1002
			    			$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>';
1003
			    		$html .= '</p>';
1004
1005
			    		// Question media
1006
						$html .= '<p>';
1007
					    	$html .= '<label for="question_add_new_media_button">' . __( 'Question media:', 'woothemes-sensei' ) . '</label><br/>';
1008
					    	$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>';
1009
					    	$html .= '<button id="question_add_new_media_button_delete" class="delete_media_file_button button-secondary hidden">' . __( 'Delete file', 'woothemes-sensei' ) . '</button><br/>';
1010
					    	$html .= '<span id="question_add_new_media_link" class="question_media_link hidden"></span>';
1011
					    	$html .= '<br/><img id="question_add_new_media_preview" class="question_media_preview hidden" src="" /><br/>';
1012
					    	$html .= '<input type="hidden" id="question_add_new_media" class="question_media" name="question_media" value="" />';
1013
				    	$html .= '</p>';
1014
1015
					$html .= '</div>';
1016
				$html .= '</div>';
1017
1018
				foreach ( $question_types as $type => $label ) {
1019
					$html .= $this->quiz_panel_question_field( $type );
1020
				}
1021
1022
				if( 'quiz' == $context ) {
1023
					$html .= '<div class="add-question">';
1024
			    		$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>';
1025
		    		$html .= '</div>';
1026
		    	}
1027
1028
		    $html .= '</div>';
1029
1030
		    if( 'quiz' == $context ) {
1031
1032
			    $html .= '<div class="tab-content hidden" id="tab-existing-content">';
1033
1034
			    	$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>';
1035
1036
			    	$html .= '<div id="existing-filters" class="alignleft actions">
1037
			    				<select id="existing-status">
1038
			    					<option value="all">' . __( 'All', 'woothemes-sensei' ) . '</option>
1039
			    					<option value="unused">' . __( 'Unused', 'woothemes-sensei' ) . '</option>
1040
			    					<option value="used">' . __( 'Used', 'woothemes-sensei' ) . '</option>
1041
			    				</select>
1042
			    				<select id="existing-type">
1043
			    					<option value="">' . __( 'All Types', 'woothemes-sensei' ) . '</option>';
1044 View Code Duplication
							    	foreach ( $question_types as $type => $label ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1045
										$html .= '<option value="' . esc_attr( $type ) . '">' . esc_html( $label ) . '</option>';
1046
									}
1047
    				$html .= '</select>
1048
    							<select id="existing-category">
1049
			    					<option value="">' . __( 'All Categories', 'woothemes-sensei' ) . '</option>';
1050 View Code Duplication
				    				foreach( $question_cats as $cat ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1051
										$html .= '<option value="' . esc_attr( $cat->slug ) . '">' . esc_html( $cat->name ) . '</option>';
1052
									}
1053
    				$html .= '</select>
1054
    							<input type="text" id="existing-search" placeholder="' . __( 'Search', 'woothemes-sensei' ) . '" />
1055
    							<a class="button" id="existing-filter-button">' . __( 'Filter', 'woothemes-sensei' ) . '</a>
1056
			    			</div>';
1057
1058
			    	$html .= '<table id="existing-table" class="widefat">';
1059
1060
			    		$html .= '<thead>
1061
									    <tr>
1062
									        <th scope="col" class="column-cb check-column"><input type="checkbox" /></th>
1063
									        <th scope="col">' . __( 'Question', 'woothemes-sensei' ) . '</th>
1064
									        <th scope="col">' . __( 'Type', 'woothemes-sensei' ) . '</th>
1065
									        <th scope="col">' . __( 'Category', 'woothemes-sensei' ) . '</th>
1066
									    </tr>
1067
									</thead>
1068
									<tfoot>
1069
									    <tr>
1070
										    <th scope="col" class="check-column"><input type="checkbox" /></th>
1071
									        <th scope="col">' . __( 'Question', 'woothemes-sensei' ) . '</th>
1072
									        <th scope="col">' . __( 'Type', 'woothemes-sensei' ) . '</th>
1073
									        <th scope="col">' . __( 'Category', 'woothemes-sensei' ) . '</th>
1074
									    </tr>
1075
									</tfoot>';
1076
						$html .= '<tbody id="existing-questions">';
1077
1078
						$questions = $this->quiz_panel_get_existing_questions();
1079
1080
						$row = 1;
1081
						foreach( $questions['questions'] as $question ) {
1082
							$html .= $this->quiz_panel_add_existing_question( $question->ID, $row );
1083
							++$row;
1084
						}
1085
1086
						$html .= '</tbody>';
1087
1088
			    	$html .= '</table>';
1089
1090
			    	$next_class = '';
1091
			    	if( $questions['count'] <= 10 ) {
1092
			    		$next_class = 'hidden';
1093
			    	}
1094
1095
			    	$html .= '<div id="existing-pagination">';
1096
			    		$html .= '<input type="hidden" id="existing-page" value="1" />';
1097
			    		$html .= '<a class="prev no-paging">&larr; ' . __( 'Previous', 'woothemes-sensei') . '</a> <a class="next ' . esc_attr( $next_class ) . '">' . __( 'Next', 'woothemes-sensei') . ' &rarr;</a>';
1098
			    	$html .= '</div>';
1099
1100
			    	$html .= '<div class="existing-actions">';
1101
			    		$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>';
1102
			    	$html .= '</div>';
1103
1104
			    $html .= '</div>';
1105
1106
			    if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats ) ) {
1107
				    $html .= '<div class="tab-content hidden" id="tab-multiple-content">';
1108
1109
				    	$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>';
1110
1111
						$html .= '<p><select id="add-multiple-question-category-options" name="multiple_category" class="chosen_select widefat question-category-select">' . "\n";
1112
						$html .= '<option value="">' . __( 'Select a Question Category', 'woothemes-sensei' ) . '</option>' . "\n";
1113 View Code Duplication
						foreach( $question_cats as $cat ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1114
							$html .= '<option value="' . esc_attr( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
1115
						} // End For Loop
1116
						$html .= '</select></p>' . "\n";
1117
1118
						$html .= '<p>' . __( 'Number of questions:', 'woothemes-sensei' ) . ' <input type="number" min="1" value="1" max="1" id="add-multiple-question-count" class="small-text"/>';
1119
1120
						$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>';
1121
1122
				    $html .= '</div>';
1123
				}
1124
			}
1125
1126
		$html .= '</div>';
1127
1128
		return $html;
1129
	}
1130
1131
	public function quiz_panel_get_existing_questions( $question_status = 'all', $question_type = '', $question_category = '', $question_search = '', $page = 1 ) {
1132
1133
		$args = array(
1134
			'post_type' => 'question',
1135
			'posts_per_page' => 10,
1136
			'post_status' => 'publish',
1137
			'suppress_filters' => 0,
1138
		);
1139
1140
		switch( $question_status ) {
1141
			case 'unused': $quiz_status = 'NOT EXISTS'; break;
1142
			case 'used': $quiz_status = 'EXISTS'; break;
1143
			default: $quiz_status = ''; break;
1144
		}
1145
1146
		if( $quiz_status ) {
1147
			switch( $quiz_status ) {
1148
				case 'EXISTS':
1149
					$args['meta_query'][] = array(
1150
						'key' => '_quiz_id',
1151
						'compare' => $quiz_status,
1152
					);
1153
				break;
1154
1155
				case 'NOT EXISTS':
1156
					$args['meta_query'][] = array(
1157
						'key' => '_quiz_id',
1158
						'value' => 'bug #23268',
1159
						'compare' => $quiz_status,
1160
					);
1161
				break;
1162
			}
1163
		}
1164
1165 View Code Duplication
		if( $question_type ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1166
			$args['tax_query'][] = array(
1167
				'taxonomy' => 'question-type',
1168
				'field' => 'slug',
1169
				'terms' => $question_type,
1170
			);
1171
		}
1172
1173 View Code Duplication
		if( $question_category ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1174
			$args['tax_query'][] = array(
1175
				'taxonomy' => 'question-category',
1176
				'field' => 'slug',
1177
				'terms' => $question_category,
1178
			);
1179
		}
1180
1181
		if( $question_type && $question_category ) {
1182
			$args['tax_query']['relation'] = 'AND';
1183
		}
1184
1185
		if( $question_search ) {
1186
			$args['s'] = $question_search;
1187
		}
1188
1189
		if( $page ) {
1190
			$args['paged'] = $page;
1191
		}
1192
1193
		$qry = new WP_Query( $args );
1194
1195
        /**
1196
         * Filter existing questions query
1197
         *
1198
         * @since 1.8.0
1199
         *
1200
         * @param WP_Query $wp_query
1201
         */
1202
        $qry = apply_filters( 'sensei_existing_questions_query_results', $qry );
1203
1204
		$questions['questions'] = $qry->posts;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$questions was never initialized. Although not strictly required by PHP, it is generally a good practice to add $questions = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
1205
		$questions['count'] = intval( $qry->found_posts );
1206
		$questions['page'] = $page;
1207
1208
		return $questions;
1209
	}
1210
1211
	public function quiz_panel_add_existing_question( $question_id = 0, $row = 1 ) {
1212
1213
		$html = '';
1214
1215
		if( ! $question_id ) {
1216
1217
            return;
1218
1219
        }
1220
1221
		$existing_class = '';
1222
		if( $row % 2 ) {
1223
            $existing_class = 'alternate';
1224
        }
1225
1226
		$question_type = Sensei()->question->get_question_type( $question_id );
1227
1228
		$question_cat_list = strip_tags( get_the_term_list( $question_id, 'question-category', '', ', ', '' ) );
1229
1230
		$html .= '<tr class="' . esc_attr( $existing_class ) . '">
1231
					<td class="cb"><input type="checkbox" value="' . $question_id . '" class="existing-item" /></td>
1232
					<td>' . get_the_title( $question_id ) . '</td>
1233
					<td>' . esc_html( $question_type ) . '</td>
1234
					<td>' . esc_html( $question_cat_list ) . '</td>
1235
				  </tr>';
1236
1237
		return $html;
1238
1239
	}
1240
1241
	public function quiz_panel_filter_existing_questions() {
1242
1243
		$return = '';
1244
1245
		//Add nonce security to the request
1246
		$nonce = '';
1247
		if( isset( $_POST['filter_existing_questions_nonce'] ) ) {
1248
			$nonce = esc_html( $_POST['filter_existing_questions_nonce'] );
1249
		} // End If Statement
1250
1251
		if( ! wp_verify_nonce( $nonce, 'filter_existing_questions_nonce' ) ) {
1252
			die( $return );
1253
		} // End If Statement
1254
1255
		// Parse POST data
1256
		$data = $_POST['data'];
1257
		$question_data = array();
1258
		parse_str( $data, $question_data );
1259
1260
		if( 0 < count( $question_data ) ) {
1261
1262
			$question_status = '';
1263
			if( isset( $question_data['question_status'] ) ) {
1264
				$question_status = $question_data['question_status'];
1265
			}
1266
1267
			$question_type = '';
1268
			if( isset( $question_data['question_type'] ) ) {
1269
				$question_type = $question_data['question_type'];
1270
			}
1271
1272
			$question_category = '';
1273
			if( isset( $question_data['question_category'] ) ) {
1274
				$question_category = $question_data['question_category'];
1275
			}
1276
1277
			$question_search = '';
1278
			if( isset( $question_data['question_search'] ) ) {
1279
				$question_search = $question_data['question_search'];
1280
			}
1281
1282
			$question_page = 1;
1283
			if( isset( $question_data['question_page'] ) ) {
1284
				$question_page = intval( $question_data['question_page'] );
1285
			}
1286
1287
			$questions = $this->quiz_panel_get_existing_questions( $question_status, $question_type, $question_category, $question_search, $question_page );
1288
1289
			$row = 1;
1290
			$html = '';
1291
			foreach( $questions['questions'] as $question ) {
1292
				$html .= $this->quiz_panel_add_existing_question( $question->ID, $row );
1293
				++$row;
1294
			}
1295
1296
			if( ! $html ) {
1297
				$html = '<tr class="alternate">
1298
								<td class="no-results" colspan="4"><em>' . __( 'There are no questions matching your search.', 'woothemes-sensei' ) . '</em></td>
1299
							  </tr>';
1300
			}
1301
1302
			$return['html'] = $html;
1303
			$return['count'] = $questions['count'];
1304
			$return['page'] = $questions['page'];
1305
1306
			wp_send_json( $return );
1307
		}
1308
1309
		die( $return );
1310
	}
1311
1312
	public function quiz_panel_question_field( $question_type = '', $question_id = 0, $question_counter = 0 ) {
1313
1314
		$html = '';
1315
1316
		if( $question_type ) {
1317
1318
			$right_answer = '';
1319
			$wrong_answers = array();
1320
			$answer_order_string = '';
1321
			$answer_order = array();
1322
			if( $question_id ) {
1323
				$right_answer = get_post_meta( $question_id, '_question_right_answer', true);
1324
				$wrong_answers = get_post_meta( $question_id, '_question_wrong_answers', true);
1325
				$answer_order_string = get_post_meta( $question_id, '_answer_order', true );
1326
				$answer_order = array_filter( explode( ',', $answer_order_string ) );
1327
				$question_class = '';
1328
			} else {
1329
				$question_id = '';
1330
				$question_class = 'answer-fields question_required_fields hidden';
1331
			}
1332
1333
			switch ( $question_type ) {
1334
				case 'multiple-choice':
1335
					$html .= '<div class="question_default_fields multiple-choice-answers ' . str_replace( ' hidden', '', $question_class ) . '">';
1336
1337
						$right_answers = (array) $right_answer;
1338
						// Calculate total right answers available (defaults to 1)
1339
						$total_right = 0;
1340
						if( $question_id ) {
1341
							$total_right = get_post_meta( $question_id, '_right_answer_count', true );
1342
						}
1343
						if( 0 == intval( $total_right ) ) {
1344
							$total_right = 1;
1345
						}
1346
						for ( $i = 0; $i < $total_right; $i++ ) {
1347
							if ( !isset( $right_answers[ $i ] ) ) { $right_answers[ $i ] = ''; }
1348
							$right_answer_id = $this->get_answer_id( $right_answers[ $i ] );
1349
							// Right Answer
1350
							$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>';
1351
							if( $question_id ) {
1352
								$answers[ $right_answer_id ] = $right_answer;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$answers was never initialized. Although not strictly required by PHP, it is generally a good practice to add $answers = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
1353
							} else {
1354
								$answers[] = $right_answer;
0 ignored issues
show
Bug introduced by
The variable $answers does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1355
							}
1356
						}
1357
1358
				    	// Calculate total wrong answers available (defaults to 4)
1359
				    	$total_wrong = 0;
1360
				    	if( $question_id ) {
1361
				    		$total_wrong = get_post_meta( $question_id, '_wrong_answer_count', true );
1362
				    	}
1363
				    	if( 0 == intval( $total_wrong ) ) {
1364
				    		$total_wrong = 1;
1365
				    	}
1366
1367
                        // Setup Wrong Answer HTML
1368
                        foreach ( $wrong_answers as $i => $answer ){
1369
1370
                            $answer_id = $this->get_answer_id( $answer );
1371
                            $wrong_answer = '<label class="answer" for="question_' . $question_counter . '_wrong_answer_' . $i . '"><span>' . __( 'Wrong:' , 'woothemes-sensei' ) ;
1372
                            $wrong_answer .= '</span> <input rel="' . esc_attr( $answer_id ) . '" type="text" id="question_' . $question_counter . '_wrong_answer_' . $i ;
1373
                            $wrong_answer .= '" name="question_wrong_answers[]" value="' . esc_attr( $answer ) . '" size="25" class="question_answer widefat" /> <a class="remove_answer_option"></a></label>';
1374
                            if( $question_id ) {
1375
1376
                                $answers[ $answer_id ] = $wrong_answer;
1377
1378
                            } else {
1379
1380
                                $answers[] = $wrong_answer;
1381
1382
                            }
1383
1384
                        } // end for each
1385
1386
				    	$answers_sorted = $answers;
1387 View Code Duplication
				    	if( $question_id && count( $answer_order ) > 0 ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1388
				    		$answers_sorted = array();
1389
				    		foreach( $answer_order as $answer_id ) {
1390
				    			if( isset( $answers[ $answer_id ] ) ) {
1391
				    				$answers_sorted[ $answer_id ] = $answers[ $answer_id ];
1392
				    				unset( $answers[ $answer_id ] );
1393
				    			}
1394
				    		}
1395
1396
				    		if( count( $answers ) > 0 ) {
1397
						    	foreach( $answers as $id => $answer ) {
1398
						    		$answers_sorted[ $id ] = $answer;
1399
						    	}
1400
						    }
1401
				    	}
1402
1403
						foreach( $answers_sorted as $id => $answer ) {
1404
				    		$html .= $answer;
1405
				    	}
1406
1407
				    	$html .= '<input type="hidden" class="answer_order" name="answer_order" value="' . $answer_order_string . '" />';
1408
				    	$html .= '<span class="hidden right_answer_count">' . $total_right . '</span>';
1409
				    	$html .= '<span class="hidden wrong_answer_count">' . $total_wrong . '</span>';
1410
1411
				    	$html .= '<div class="add_answer_options">';
1412
					    	$html .= '<a class="add_right_answer_option add_answer_option button" rel="' . $question_counter . '">' . __( 'Add right answer', 'woothemes-sensei' ) . '</a>';
1413
					    	$html .= '<a class="add_wrong_answer_option add_answer_option button" rel="' . $question_counter . '">' . __( 'Add wrong answer', 'woothemes-sensei' ) . '</a>';
1414
				    	$html .= '</div>';
1415
1416
                        $html .= $this->quiz_panel_question_feedback( $question_counter, $question_id , 'multiple-choice' );
1417
1418
			    	$html .= '</div>';
1419
				break;
1420
				case 'boolean':
1421
					$html .= '<div class="question_boolean_fields ' . $question_class . '">';
1422
						if( $question_id ) {
1423
							$field_name = 'question_' . $question_id . '_right_answer_boolean';
1424
						} else {
1425
							$field_name = 'question_right_answer_boolean';
1426
							$right_answer = 'true';
1427
						}
1428
						$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>';
1429
						$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>';
1430
1431
                    $html .= $this->quiz_panel_question_feedback( $question_counter, $question_id, 'boolean' );
1432
1433
					$html .= '</div>';
1434
				break;
1435
				case 'gap-fill':
1436
					$gapfill_array = explode( '||', $right_answer );
1437
					if ( isset( $gapfill_array[0] ) ) { $gapfill_pre = $gapfill_array[0]; } else { $gapfill_pre = ''; }
1438
					if ( isset( $gapfill_array[1] ) ) { $gapfill_gap = $gapfill_array[1]; } else { $gapfill_gap = ''; }
1439
					if ( isset( $gapfill_array[2] ) ) { $gapfill_post = $gapfill_array[2]; } else { $gapfill_post = ''; }
1440
					$html .= '<div class="question_gapfill_fields ' . $question_class . '">';
1441
						// Fill in the Gaps
1442
						$html .= '<label>' . __( 'Text before the Gap:' , 'woothemes-sensei' ) . '</label> ';
1443
						$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" />';
1444
	  					$html .= '<label>' . __( 'The Gap:' , 'woothemes-sensei' ) . '</label> ';
1445
	  					$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" />';
1446
	  					$html .= '<label>' . __( 'Text after the Gap:' , 'woothemes-sensei' ) . '</label> ';
1447
	  					$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" />';
1448
	  					$html .= '<label>' . __( 'Preview:' , 'woothemes-sensei' ) . '</label> ';
1449
	  					$html .= '<p class="gapfill-preview">' . $gapfill_pre . '&nbsp;<u>' . $gapfill_gap . '</u>&nbsp;' . $gapfill_post . '</p>';
1450
	  				$html .= '</div>';
1451
				break;
1452 View Code Duplication
				case 'multi-line':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1453
					$html .= '<div class="question_multiline_fields ' . $question_class . '">';
1454
						// Guides for grading
1455
						if( $question_counter ) {
1456
							$field_id = 'question_' . $question_counter . '_add_question_right_answer_multiline';
1457
						} else {
1458
							$field_id = 'add_question_right_answer_multiline';
1459
						}
1460
						$html .= '<label>' . __( 'Guide/Teacher Notes for grading the answer' , 'woothemes-sensei' ) . '</label> ';
1461
						$html .= '<textarea id="' . $field_id . '" name="add_question_right_answer_multiline" rows="4" cols="40" class="widefat">' . $right_answer . '</textarea>';
1462
					$html .= '</div>';
1463
				break;
1464 View Code Duplication
				case 'single-line':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1465
					$html .= '<div class="question_singleline_fields ' . $question_class . '">';
1466
						// Recommended Answer
1467
						if( $question_counter ) {
1468
							$field_id = 'question_' . $question_counter . '_add_question_right_answer_singleline';
1469
						} else {
1470
							$field_id = 'add_question_right_answer_singleline';
1471
						}
1472
						$html .= '<label>' . __( 'Recommended Answer' , 'woothemes-sensei' ) . '</label> ';
1473
						$html .= '<input type="text" id="' . $field_id . '" name="add_question_right_answer_singleline" value="' . $right_answer . '" size="25" class="widefat" />';
1474
					$html .= '</div>';
1475
				break;
1476
				case 'file-upload':
1477
					$html .= '<div class="question_fileupload_fields ' . $question_class . '">';
1478
						if( $question_counter ) {
1479
							$right_field_id = 'question_' . $question_counter . '_add_question_right_answer_fileupload';
1480
							$wrong_field_id = 'question_' . $question_counter . '_add_question_wrong_answer_fileupload';
1481
						} else {
1482
							$right_field_id = 'add_question_right_answer_fileupload';
1483
							$wrong_field_id = 'add_question_wrong_answer_fileupload';
1484
						}
1485
1486
						$wrong_answer = '';
1487
						if( isset( $wrong_answers[0] ) ) {
1488
							$wrong_answer = $wrong_answers[0];
1489
						}
1490
						$html .= '<label>' . __( 'Description for student explaining what needs to be uploaded' , 'woothemes-sensei' ) . '</label> ';
1491
						$html .= '<textarea id="' . $wrong_field_id . '" name="add_question_wrong_answer_fileupload" rows="4" cols="40" class="widefat">' . $wrong_answer . '</textarea>';
1492
1493
						// Guides for grading
1494
						$html .= '<label>' . __( 'Guide/Teacher Notes for grading the upload' , 'woothemes-sensei' ) . '</label> ';
1495
						$html .= '<textarea id="' . $right_field_id . '" name="add_question_right_answer_fileupload" rows="4" cols="40" class="widefat">' . $right_answer . '</textarea>';
1496
					$html .= '</div>';
1497
				break;
1498
			}
1499
		}
1500
1501
		return $html;
1502
	}
1503
1504
	public function quiz_panel_question_feedback( $question_counter = 0, $question_id = 0, $question_type = '' ) {
1505
1506
        // default field name
1507
        $field_name = 'answer_feedback';
1508
        if( 'boolean' == $question_type ){
1509
1510
            $field_name = 'answer_feedback_boolean';
1511
1512
        }elseif( 'multiple-choice' == $question_type ){
1513
1514
            $field_name = 'answer_feedback_multiple_choice';
1515
1516
        }// end if
1517
1518
		if( $question_counter ) {
1519
			$field_name = 'answer_' . $question_counter . '_feedback';
1520
		}
1521
1522
		$feedback = '';
1523
		if( $question_id ) {
1524
			$feedback = get_post_meta( $question_id, '_answer_feedback', true );
1525
		}
1526
1527
		$html = '<p title="' . __( 'This feedback will be automatically displayed to the student once they have completed the quiz.', 'woothemes-sensei' ) . '">';
1528
		$html .= '<label for="' . $field_name . '">' . __( 'Answer Feedback' , 'woothemes-sensei' ) . ':</label>';
1529
		$html .= '<textarea id="' . $field_name . '" name="' . $field_name . '" rows="4" cols="40" class="answer_feedback widefat">' . $feedback . '</textarea>';
1530
		$html .= '</p>';
1531
1532
		return $html;
1533
	}
1534
1535
	public function question_get_answer_id() {
1536
		$data = $_POST['data'];
1537
		$answer_data = array();
1538
		parse_str( $data, $answer_data );
1539
		$answer = $answer_data['answer_value'];
1540
		$answer_id = $this->get_answer_id( $answer );
1541
		echo $answer_id;
1542
		die();
1543
	}
1544
1545
	public function get_answer_id( $answer = '' ) {
1546
1547
		$answer_id = '';
1548
1549
		if( $answer ) {
1550
			$answer_id = md5( $answer );
1551
		}
1552
1553
		return $answer_id;
1554
1555
	}
1556
1557
	/**
1558
	 * lesson_quiz_meta_box_content function.
1559
	 *
1560
	 * @access public
1561
	 * @return void
1562
	 */
1563
	public function lesson_quiz_meta_box_content () {
1564
		global $post;
1565
1566
		// Get quiz panel
1567
		$quiz_id = 0;
1568
		$quizzes = array();
0 ignored issues
show
Unused Code introduced by
$quizzes 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...
1569
		if ( 0 < $post->ID ) {
1570
			$quiz_id = $this->lesson_quizzes( $post->ID, 'any' );
1571
		}
1572
1573
		echo $this->quiz_panel( $quiz_id );
1574
1575
	} // End lesson_quiz_meta_box_content()
1576
1577
	/**
1578
	 * Quiz settings metabox
1579
	 * @return void
1580
	 */
1581
	public function lesson_quiz_settings_meta_box_content() {
1582
		global $post;
1583
1584
		$html = '';
1585
1586
		// Get quiz panel
1587
		$quiz_id = 0;
1588
		$lesson_id = $post->ID;
1589
		$quizzes = array();
0 ignored issues
show
Unused Code introduced by
$quizzes 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...
1590
		if ( 0 < $lesson_id ) {
1591
			$quiz_id = $this->lesson_quizzes( $lesson_id, 'any' );
1592
		}
1593
1594
		if( $quiz_id ) {
1595
			$html .= $this->quiz_settings_panel( $lesson_id, $quiz_id );
1596
		} else {
1597
			$html .= '<p><em>' . __( 'There is no quiz for this lesson yet - please add one in the \'Quiz Questions\' box.', 'woothemes-sensei' ) . '</em></p>';
1598
		}
1599
1600
		echo $html;
1601
	}
1602
1603
	public function quiz_settings_panel( $lesson_id = 0, $quiz_id = 0 ) {
1604
1605
1606
		$html = '';
1607
1608
		if( ! $lesson_id && ! $quiz_id ) return $html;
1609
1610
		$settings = $this->get_quiz_settings( $quiz_id );
1611
1612
		$html = Sensei()->admin->render_settings( $settings, $quiz_id, 'quiz-settings' );
1613
1614
		return $html;
1615
1616
	}
1617
1618
	public function get_quiz_settings( $quiz_id = 0 ) {
1619
1620
		$disable_passmark = '';
1621
		$pass_required = get_post_meta( $quiz_id, '_pass_required', true );
1622
		if( ! $pass_required ) {
1623
			$disable_passmark = 'hidden';
1624
		}
1625
1626
		// Setup Questions Query
1627
		$questions = array();
1628
		if ( 0 < $quiz_id ) {
1629
			$questions = $this->lesson_quiz_questions( $quiz_id );
1630
		}
1631
1632
		// Count questions
1633
		$question_count = 0;
1634 View Code Duplication
		foreach( $questions as $question ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1635
			if( $question->post_type == 'multiple_question' ) {
1636
				$question_number = get_post_meta( $question->ID, 'number', true );
1637
				$question_count += $question_number;
1638
			} else {
1639
				++$question_count;
1640
			}
1641
		}
1642
1643
		$settings = array(
1644
			array(
1645
				'id' 			=> 'pass_required',
1646
				'label'			=> __( 'Pass required to complete lesson', 'woothemes-sensei' ),
1647
				'description'	=> __( 'The passmark must be achieved before the lesson is complete.', 'woothemes-sensei' ),
1648
				'type'			=> 'checkbox',
1649
				'default'		=> '',
1650
				'checked'		=> 'on',
1651
			),
1652
			array(
1653
				'id' 			=> 'quiz_passmark',
1654
				'label'			=> __( 'Quiz passmark percentage', 'woothemes-sensei' ),
1655
				'description'	=> '',
1656
				'type'			=> 'number',
1657
				'default'		=> 0,
1658
				'placeholder'	=> 0,
1659
				'min'			=> 0,
1660
				'max'			=> 100,
1661
				'class'			=> $disable_passmark,
1662
			),
1663
			array(
1664
				'id' 			=> 'show_questions',
1665
				'label'			=> __( 'Number of questions to show', 'woothemes-sensei' ),
1666
				'description'	=> __( 'Show a random selection of questions from this quiz each time a student views it.', 'woothemes-sensei' ),
1667
				'type'			=> 'number',
1668
				'default'		=> '',
1669
				'placeholder'	=> __( 'All', 'woothemes-sensei' ),
1670
				'min'			=> 1,
1671
				'max'			=> $question_count,
1672
			),
1673
			array(
1674
				'id' 			=> 'random_question_order',
1675
				'label'			=> __( 'Randomise question order', 'woothemes-sensei' ),
1676
				'description'	=> '',
1677
				'type'			=> 'checkbox',
1678
				'default'		=> 'no',
1679
				'checked'		=> 'yes',
1680
			),
1681
			array(
1682
				'id' 			=> 'quiz_grade_type',
1683
				'label'			=> __( 'Grade quiz automatically', 'woothemes-sensei' ),
1684
				'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' ),
1685
				'type'			=> 'checkbox',
1686
				'default'		=> 'auto',
1687
				'checked'		=> 'auto',
1688
			),
1689
			array(
1690
				'id' 			=> 'enable_quiz_reset',
1691
				'label'			=> __( 'Allow user to retake the quiz', 'woothemes-sensei' ),
1692
				'description'	=> __( 'Enables the quiz reset button.', 'woothemes-sensei' ),
1693
				'type'			=> 'checkbox',
1694
				'default'		=> '',
1695
				'checked'		=> 'on',
1696
			),
1697
		);
1698
1699
		return apply_filters( 'sensei_quiz_settings', $settings );
1700
	}
1701
1702
	/**
1703
	 * enqueue_scripts function.
1704
	 *
1705
	 * @access public
1706
	 * @return void
1707
	 */
1708
	public function enqueue_scripts( $hook ) {
1709
		global  $post_type;
1710
1711
		$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question' ) );
1712
		$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
1713
		$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', 'sensei_analysis', 'sensei_learners', 'sensei_updates', 'woothemes-sensei-settings', 'lesson-order' ) );
1714
1715
		// Test for Write Panel Pages
1716
		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 ) ) ) {
1717
1718
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
1719
1720
			// Load the lessons script
1721
            wp_enqueue_media();
1722
			wp_enqueue_script( 'sensei-lesson-metadata', Sensei()->plugin_url . 'assets/js/lesson-metadata' . $suffix . '.js', array( 'jquery', 'select2' ,'jquery-ui-sortable' ), Sensei()->version, true );
1723
			wp_enqueue_script( 'sensei-lesson-chosen', Sensei()->plugin_url . 'assets/chosen/chosen.jquery' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
1724
			wp_enqueue_script( 'sensei-chosen-ajax', Sensei()->plugin_url . 'assets/chosen/ajax-chosen.jquery' . $suffix . '.js', array( 'jquery', 'sensei-lesson-chosen' ), Sensei()->version, true );
1725
1726
            // Load the bulk edit screen script
1727
            if( 'edit.php' == $hook && 'lesson'==$_GET['post_type'] ) {
1728
                wp_enqueue_script( 'sensei-lessons-bulk-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-bulk-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version , true);
1729
            }
1730
1731
			// Localise script
1732
			$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' ) );
1733
			$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' ) );
1734
			$data = array_merge( $translation_strings, $ajax_vars );
1735
			wp_localize_script( 'sensei-lesson-metadata', 'woo_localized_data', $data );
1736
1737
			// Chosen RTL
1738
			if ( is_rtl() ) {
1739
				wp_enqueue_script( 'sensei-chosen-rtl', Sensei()->plugin_url . 'assets/chosen/chosen-rtl' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
1740
			}
1741
1742
		}
1743
1744
	} // End enqueue_scripts()
1745
1746
	/**
1747
	 * Load in CSS styles where necessary.
1748
	 *
1749
	 * @access public
1750
	 * @since  1.4.0
1751
	 * @return void
1752
	 */
1753
	public function enqueue_styles ( $hook ) {
1754
		global  $post_type;
1755
1756
		$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question', 'sensei_message' ) );
1757
		$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
1758
		$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', 'sensei_analysis', 'sensei_learners', 'sensei_updates', 'woothemes-sensei-settings' ) );
1759
1760
		// Test for Write Panel Pages
1761 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 ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1762
			wp_enqueue_style( 'woothemes-sensei-settings-api', esc_url( Sensei()->plugin_url . 'assets/css/settings.css' ), '', Sensei()->version );
1763
		}
1764
1765
	} // End enqueue_styles()
1766
1767
	/**
1768
	 * Add column headings to the "lesson" post list screen.
1769
	 * @access public
1770
	 * @since  1.0.0
1771
	 * @param  array $defaults
1772
	 * @return array $new_columns
1773
	 */
1774 View Code Duplication
	public function add_column_headings ( $defaults ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1775
		$new_columns['cb'] = '<input type="checkbox" />';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$new_columns was never initialized. Although not strictly required by PHP, it is generally a good practice to add $new_columns = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
1776
		$new_columns['title'] = _x( 'Lesson Title', 'column name', 'woothemes-sensei' );
1777
		$new_columns['lesson-course'] = _x( 'Course', 'column name', 'woothemes-sensei' );
1778
		$new_columns['lesson-prerequisite'] = _x( 'Pre-requisite Lesson', 'column name', 'woothemes-sensei' );
1779
		if ( isset( $defaults['date'] ) ) {
1780
			$new_columns['date'] = $defaults['date'];
1781
		}
1782
		return $new_columns;
1783
	} // End add_column_headings()
1784
1785
	/**
1786
	 * Add data for our newly-added custom columns.
1787
	 * @access public
1788
	 * @since  1.0.0
1789
	 * @param  string $column_name
1790
	 * @param  int $id
1791
	 * @return void
1792
	 */
1793
	public function add_column_data ( $column_name, $id ) {
1794
		global $wpdb, $post;
1795
1796
		switch ( $column_name ) {
1797
			case 'id':
1798
				echo $id;
1799
			break;
1800 View Code Duplication
			case 'lesson-course':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1801
				$lesson_course_id = get_post_meta( $id, '_lesson_course', true);
1802
				if ( 0 < absint( $lesson_course_id ) ) {
1803
					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>';
1804
				} // End If Statement
1805
			break;
1806 View Code Duplication
			case 'lesson-prerequisite':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1807
				$lesson_prerequisite_id = get_post_meta( $id, '_lesson_prerequisite', true);
1808
				if ( 0 < absint( $lesson_prerequisite_id ) ) {
1809
					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>';
1810
				} // End If Statement
1811
			break;
1812
			default:
1813
			break;
1814
		} // End Switch Statement
1815
	} // End add_column_data()
1816
1817
	/**
1818
	 * lesson_add_course function.
1819
	 *
1820
	 * @access public
1821
	 * @return void
1822
	 */
1823
	public function lesson_add_course () {
1824
		global $current_user;
1825
		//Add nonce security to the request
1826
		if ( isset($_POST['lesson_add_course_nonce']) ) {
1827
			$nonce = esc_html( $_POST['lesson_add_course_nonce'] );
1828
		} // End If Statement
1829
		if ( ! wp_verify_nonce( $nonce, 'lesson_add_course_nonce' ) ) {
0 ignored issues
show
Bug introduced by
The variable $nonce does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1830
			die('');
1831
		} // End If Statement
1832
		// Parse POST data
1833
		$data = $_POST['data'];
1834
		$course_data = array();
1835
		parse_str($data, $course_data);
1836
		// Save the Course
1837
		$updated = false;
0 ignored issues
show
Unused Code introduced by
$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...
1838
		$current_user = wp_get_current_user();
1839
		$question_data['post_author'] = $current_user->ID;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$question_data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $question_data = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
1840
		$updated = $this->lesson_save_course($course_data);
0 ignored issues
show
Bug introduced by
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...
1841
		echo $updated;
1842
		die(); // WordPress may print out a spurious zero without this can be particularly bad if using JSON
1843
	} // End lesson_add_course()
1844
1845
	/**
1846
	 * lesson_update_question function.
1847
	 *
1848
	 * @access public
1849
	 * @return void
1850
	 */
1851
	public function lesson_update_question () {
1852
		global $current_user;
1853
		//Add nonce security to the request
1854
		if ( isset($_POST['lesson_update_question_nonce']) ) {
1855
			$nonce = esc_html( $_POST['lesson_update_question_nonce'] );
1856
		} // End If Statement
1857
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_question_nonce' ) ) {
0 ignored issues
show
Bug introduced by
The variable $nonce does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1858
			die('');
1859
		} // End If Statement
1860
		// Parse POST data
1861
		// WP slashes all incoming data regardless of Magic Quotes setting (see wp_magic_quotes()), which means that
1862
		// even the $_POST['data'] encoded with encodeURIComponent has it's apostrophes slashed.
1863
		// So first restore the original unslashed apostrophes by removing those slashes
1864
		$data = wp_unslash( $_POST['data'] );
1865
		// Then parse the string to an array (note that parse_str automatically urldecodes all the variables)
1866
		$question_data = array();
1867
		parse_str($data, $question_data);
1868
		// Finally re-slash all elements to ensure consistancy for lesson_save_question()
1869
		$question_data = wp_slash( $question_data );
1870
		// Save the question
1871
		$return = false;
1872
		// Question Save and Delete logic
1873
		if ( isset( $question_data['action'] ) && ( $question_data['action'] == 'delete' ) ) {
1874
			// Delete the Question
1875
			$return = $this->lesson_delete_question($question_data);
1876
		} else {
1877
			// Save the Question
1878
			if ( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1879
				$current_user = wp_get_current_user();
1880
				$question_data['post_author'] = $current_user->ID;
1881
				$question_id = $this->lesson_save_question( $question_data );
1882
				$question_type = Sensei()->question->get_question_type( $question_id );
0 ignored issues
show
Bug introduced by
It seems like $question_id defined by $this->lesson_save_question($question_data) on line 1881 can also be of type boolean; however, Sensei_Question::get_question_type() does only seem to accept integer, 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...
1883
1884
				$question_count = intval( $question_data['question_count'] );
1885
				++$question_count;
1886
1887
				$return = $this->quiz_panel_question( $question_type, $question_count, $question_id );
0 ignored issues
show
Bug introduced by
It seems like $question_id defined by $this->lesson_save_question($question_data) on line 1881 can also be of type boolean; however, Sensei_Lesson::quiz_panel_question() does only seem to accept integer, 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...
1888
			} // End If Statement
1889
		} // End If Statement
1890
1891
		echo $return;
1892
1893
		die();
1894
	} // End lesson_update_question()
1895
1896
	public function lesson_add_multiple_questions() {
1897
1898
		$return = '';
1899
1900
		//Add nonce security to the request
1901
		$nonce = '';
1902
		if( isset( $_POST['lesson_add_multiple_questions_nonce'] ) ) {
1903
			$nonce = esc_html( $_POST['lesson_add_multiple_questions_nonce'] );
1904
		} // End If Statement
1905
1906
		if( ! wp_verify_nonce( $nonce, 'lesson_add_multiple_questions_nonce' ) ) {
1907
			die( $return );
1908
		} // End If Statement
1909
1910
		// Parse POST data
1911
		$data = $_POST['data'];
1912
		$question_data = array();
1913
		parse_str( $data, $question_data );
1914
1915
		if( is_array( $question_data ) ) {
1916
			if( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1917
1918
				$quiz_id = intval( $question_data['quiz_id'] );
1919
				$question_number = intval( $question_data['question_number'] );
1920
				$question_category = intval( $question_data['question_category'] );
1921
1922
				$question_counter = intval( $question_data['question_count'] );
1923
				++$question_counter;
1924
1925
				$cat = get_term( $question_category, 'question-category' );
1926
1927
				$post_data = array(
1928
					'post_content' => '',
1929
					'post_status' => 'publish',
1930
					'post_title' => sprintf( __( '%1$s Question(s) from %2$s', 'woothemes-sensei' ), $question_number, $cat->name ),
1931
					'post_type' => 'multiple_question'
1932
				);
1933
1934
				$multiple_id = wp_insert_post( $post_data );
1935
1936
				if( $multiple_id && ! is_wp_error( $multiple_id ) ) {
1937
					add_post_meta( $multiple_id, 'category', $question_category );
1938
					add_post_meta( $multiple_id, 'number', $question_number );
1939
					add_post_meta( $multiple_id, '_quiz_id', $quiz_id, false );
1940
					add_post_meta( $multiple_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_counter );
1941
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
1942
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
1943
					$return = $this->quiz_panel_question( 'category', $question_counter, $multiple_id, 'quiz', array( $cat->name, $question_number ) );
1944
				}
1945
			}
1946
		}
1947
1948
		echo $return;
1949
1950
		die();
1951
	}
1952
1953
	public function lesson_remove_multiple_questions() {
1954
1955
		//Add nonce security to the request
1956
		$nonce = '';
1957
		if( isset( $_POST['lesson_remove_multiple_questions_nonce'] ) ) {
1958
			$nonce = esc_html( $_POST['lesson_remove_multiple_questions_nonce'] );
1959
		} // End If Statement
1960
1961
		if( ! wp_verify_nonce( $nonce, 'lesson_remove_multiple_questions_nonce' ) ) {
1962
			die('');
1963
		} // End If Statement
1964
1965
		// Parse POST data
1966
		$data = $_POST['data'];
1967
		$question_data = array();
1968
		parse_str( $data, $question_data );
1969
1970
		if( is_array( $question_data ) ) {
1971
			wp_delete_post( $question_data['question_id'], true );
1972
		}
1973
1974
		die( 'Deleted' );
1975
	}
1976
1977
	public function get_question_category_limit() {
1978
1979
		// Set default
1980
		$return = 1;
1981
1982
		// Parse POST data
1983
		$data = $_POST['data'];
1984
		$cat_data = array();
1985
		parse_str( $data, $cat_data );
1986
1987
		if( isset( $cat_data['cat'] ) && '' != $cat_data['cat'] ) {
1988
			$cat = get_term( $cat_data['cat'], 'question-category' );
1989
			if( isset( $cat->count ) ) {
1990
				$return = $cat->count;
1991
			}
1992
		}
1993
1994
		echo $return;
1995
1996
		die('');
1997
	}
1998
1999
	public function lesson_add_existing_questions() {
2000
2001
		//Add nonce security to the request
2002
		$nonce = '';
2003
		if( isset( $_POST['lesson_add_existing_questions_nonce'] ) ) {
2004
			$nonce = esc_html( $_POST['lesson_add_existing_questions_nonce'] );
2005
		} // End If Statement
2006
2007
		if( ! wp_verify_nonce( $nonce, 'lesson_add_existing_questions_nonce' ) ) {
2008
			die('');
2009
		} // End If Statement
2010
2011
		// Parse POST data
2012
		$data = $_POST['data'];
2013
		$question_data = array();
2014
		parse_str( $data, $question_data );
2015
2016
		$return = '';
2017
2018
		if( is_array( $question_data ) ) {
2019
2020
			if( isset( $question_data['questions'] ) && '' != $question_data['questions'] ) {
2021
2022
				$questions = explode( ',', trim( $question_data['questions'], ',' ) );
2023
				$quiz_id = $question_data['quiz_id'];
2024
				$question_count = intval( $question_data['question_count'] );
2025
2026
				foreach( $questions as $question_id ) {
2027
2028
					++$question_count;
2029
2030
					$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2031 View Code Duplication
					if( ! in_array( $quiz_id, $quizzes ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2032
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2033
						$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2034
						update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2035
			    	}
2036
2037
			    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2038
					$question_type = Sensei()->question->get_question_type( $question_id );
2039
2040
					$return .= $this->quiz_panel_question( $question_type, $question_count, $question_id );
2041
				}
2042
			}
2043
		}
2044
2045
		echo $return;
2046
2047
		die('');
2048
	}
2049
2050 View Code Duplication
	public function lesson_update_grade_type() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2051
		//Add nonce security to the request
2052
		if ( isset($_POST['lesson_update_grade_type_nonce']) ) {
2053
			$nonce = esc_html( $_POST['lesson_update_grade_type_nonce'] );
2054
		} // End If Statement
2055
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_grade_type_nonce' ) ) {
0 ignored issues
show
Bug introduced by
The variable $nonce does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2056
			die('');
2057
		} // End If Statement
2058
		// Parse POST data
2059
		$data = $_POST['data'];
2060
		$quiz_data = array();
2061
		parse_str($data, $quiz_data);
2062
		update_post_meta( $quiz_data['quiz_id'], '_quiz_grade_type', $quiz_data['quiz_grade_type'] );
2063
		die();
2064
	}
2065
2066
	public function lesson_update_question_order() {
2067
		// Add nonce security to the request
2068
		if ( isset($_POST['lesson_update_question_order_nonce']) ) {
2069
			$nonce = esc_html( $_POST['lesson_update_question_order_nonce'] );
2070
		} // End If Statement
2071
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_question_order_nonce' ) ) {
0 ignored issues
show
Bug introduced by
The variable $nonce does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2072
			die('');
2073
		} // End If Statement
2074
		// Parse POST data
2075
		$data = $_POST['data'];
2076
		$quiz_data = array();
2077
		parse_str($data, $quiz_data);
2078
		if( strlen( $quiz_data['question_order'] ) > 0 ) {
2079
			$questions = explode( ',', $quiz_data['question_order'] );
2080
			$o = 1;
2081
			foreach( $questions as $question_id ) {
2082
				update_post_meta( $question_id, '_quiz_question_order' . $quiz_data['quiz_id'], $quiz_data['quiz_id'] . '000' . $o );
2083
				++$o;
2084
			}
2085
			update_post_meta( $quiz_data['quiz_id'], '_question_order', $questions );
2086
		}
2087
		die();
2088
	}
2089
2090 View Code Duplication
	public function lesson_update_question_order_random() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2091
		//Add nonce security to the request
2092
		if ( isset($_POST['lesson_update_question_order_random_nonce']) ) {
2093
			$nonce = esc_html( $_POST['lesson_update_question_order_random_nonce'] );
2094
		} // End If Statement
2095
		if ( ! wp_verify_nonce( $nonce, 'lesson_update_question_order_random_nonce' ) ) {
0 ignored issues
show
Bug introduced by
The variable $nonce does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2096
			die('');
2097
		} // End If Statement
2098
		// Parse POST data
2099
		$data = $_POST['data'];
2100
		$quiz_data = array();
2101
		parse_str($data, $quiz_data);
2102
		update_post_meta( $quiz_data['quiz_id'], '_random_question_order', $quiz_data['random_question_order'] );
2103
		die();
2104
	}
2105
2106
	/**
2107
	 * lesson_save_course function.
2108
	 *
2109
	 * @access private
2110
	 * @param array $data (default: array())
2111
	 * @return integer|boolean $course_id or false
2112
	 */
2113
	private function lesson_save_course( $data = array() ) {
2114
		global $current_user;
2115
		$return = false;
2116
		// Setup the course data
2117
		$course_id = 0;
2118
		$course_content = '';
2119
		$course_title = '';
2120
		$course_prerequisite = 0;
0 ignored issues
show
Unused Code introduced by
$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...
2121
		$course_category = 0;
0 ignored issues
show
Unused Code introduced by
$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...
2122 View Code Duplication
		if ( isset( $data[ 'course_id' ] ) && ( 0 < absint( $data[ 'course_id' ] ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2123
			$course_id = absint( $data[ 'course_id' ] );
2124
		} // End If Statement
2125
		if ( isset( $data[ 'course_title' ] ) && ( '' != $data[ 'course_title' ] ) ) {
2126
			$course_title = $data[ 'course_title' ];
2127
		} // End If Statement
2128
		$post_title = $course_title;
2129
		if ( isset($data[ 'post_author' ]) ) {
2130
			$post_author = $data[ 'post_author' ];
0 ignored issues
show
Unused Code introduced by
$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...
2131
		} else {
2132
			$current_user = wp_get_current_user();
2133
			$post_author = $current_user->ID;
0 ignored issues
show
Unused Code introduced by
$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...
2134
		} // End If Statement
2135
		$post_status = 'publish';
2136
		$post_type = 'course';
2137
		if ( isset( $data[ 'course_content' ] ) && ( '' != $data[ 'course_content' ] ) ) {
2138
			$course_content = $data[ 'course_content' ];
2139
		} // End If Statement
2140
		$post_content = $course_content;
2141
		// Course Query Arguments
2142
		$post_type_args = array(	'post_content' => $post_content,
2143
  		    						'post_status' => $post_status,
2144
  		    						'post_title' => $post_title,
2145
  		    						'post_type' => $post_type
2146
  		    						);
2147
  		// Only save if there is a valid title
2148
  		if ( $post_title != '' ) {
2149
  		    // Check for prerequisite courses & product id
2150
  		    $course_prerequisite_id = absint( $data[ 'course_prerequisite' ] );
2151
  		    $course_woocommerce_product_id = absint( $data[ 'course_woocommerce_product' ] );
2152
  		    $course_category_id = absint( $data[ 'course_category' ] );
2153
  		    if ( 0 == $course_woocommerce_product_id ) { $course_woocommerce_product_id = '-'; }
2154
  		    // Insert or Update the Lesson Quiz
2155
		    if ( 0 < $course_id ) {
2156
		    	$post_type_args[ 'ID' ] = $course_id;
2157
		    	$course_id = wp_update_post($post_type_args);
2158
		    	update_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2159
		    	update_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2160
		    	if ( 0 < $course_category_id ) {
2161
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2162
		    	} // End If Statement
2163
		    } else {
2164
		    	$course_id = wp_insert_post($post_type_args);
2165
		    	add_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2166
		    	add_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2167
		    	if ( 0 < $course_category_id ) {
2168
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2169
		    	} // End If Statement
2170
		    } // End If Statement
2171
		} // End If Statement
2172
  		// Check that the insert or update saved by testing the post id
2173
  		if ( 0 < $course_id ) {
2174
  			$return = $course_id;
2175
  		} // End If Statement
2176
  		return $return;
2177
  	} // End lesson_save_course()
2178
2179
2180
	/**
2181
	 * lesson_save_question function.
2182
	 *
2183
	 * @access private
2184
	 * @param array $data (default: array())
2185
	 * @return integer|boolean $question_id or false
2186
	 */
2187
	public function lesson_save_question( $data = array(), $context = 'quiz' ) {
2188
		$return = false;
2189
		// Save the Questions
2190
		// Setup the Question data
2191
		$question_id = 0;
2192
		$question_text = '';
2193
		$question_right_answer = '';
2194
		$question_wrong_answers = $question_right_answers = array();
2195
		$question_type = 'multiple-choice';
2196
		$question_category = '';
2197
2198
		// Handle Question Type
2199
		if ( isset( $data[ 'question_type' ] ) && ( '' != $data[ 'question_type' ] ) ) {
2200
			$question_type = $data[ 'question_type' ];
2201
		} // End If Statement
2202
2203
		if ( isset( $data[ 'question_category' ] ) && ( '' != $data[ 'question_category' ] ) ) {
2204
			$question_category = $data[ 'question_category' ];
2205
		} // End If Statement
2206
2207 View Code Duplication
		if ( isset( $data[ 'question_id' ] ) && ( 0 < absint( $data[ 'question_id' ] ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2208
			$question_id = absint( $data[ 'question_id' ] );
2209
		} // End If Statement
2210
		if ( isset( $data[ 'question' ] ) && ( '' != $data[ 'question' ] ) ) {
2211
			$question_text = $data[ 'question' ];
2212
		} // End If Statement
2213
		$post_title = $question_text;
0 ignored issues
show
Unused Code introduced by
$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...
2214
		// Handle Default Fields (multiple choice)
2215
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answers' ] ) && ( '' != $data[ 'question_right_answers' ] ) ) {
2216
			$question_right_answers = $data[ 'question_right_answers' ];
2217
		} // End If Statement
2218
		elseif ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answer' ] ) && ( '' != $data[ 'question_right_answer' ] ) ) {
2219
			$question_right_answer = $data[ 'question_right_answer' ];
2220
		} // End If Statement
2221
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_wrong_answers' ] ) && ( '' != $data[ 'question_wrong_answers' ] ) ) {
2222
			$question_wrong_answers = $data[ 'question_wrong_answers' ];
2223
		} // End If Statement
2224
		// Handle Boolean Fields - Edit
2225
		if ( 'boolean' == $question_type && isset( $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) && ( '' != $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) ) {
2226
			$question_right_answer = $data[ 'question_' . $question_id . '_right_answer_boolean' ];
2227
		} // End If Statement
2228
		// Handle Boolean Fields - Add
2229
		if ( 'boolean' == $question_type && isset( $data[ 'question_right_answer_boolean' ] ) && ( '' != $data[ 'question_right_answer_boolean' ] ) ) {
2230
			$question_right_answer = $data[ 'question_right_answer_boolean' ];
2231
		} // End If Statement
2232
		// Handle Gap Fill Fields
2233
		if ( 'gap-fill' == $question_type && isset( $data[ 'add_question_right_answer_gapfill_gap' ] ) && '' != $data[ 'add_question_right_answer_gapfill_gap' ] ) {
2234
			$question_right_answer = $data[ 'add_question_right_answer_gapfill_pre' ] . '||' . $data[ 'add_question_right_answer_gapfill_gap' ] . '||' . $data[ 'add_question_right_answer_gapfill_post' ];
2235
		} // End If Statement
2236
		// Handle Multi Line Fields
2237
		if ( 'multi-line' == $question_type && isset( $data[ 'add_question_right_answer_multiline' ] ) && ( '' != $data[ 'add_question_right_answer_multiline' ] ) ) {
2238
			$question_right_answer = $data[ 'add_question_right_answer_multiline' ];
2239
		} // End If Statement
2240
		// Handle Single Line Fields
2241
		if ( 'single-line' == $question_type && isset( $data[ 'add_question_right_answer_singleline' ] ) && ( '' != $data[ 'add_question_right_answer_singleline' ] ) ) {
2242
			$question_right_answer = $data[ 'add_question_right_answer_singleline' ];
2243
		} // End If Statement
2244
		// Handle File Upload Fields
2245
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_right_answer_fileupload' ] ) && ( '' != $data[ 'add_question_right_answer_fileupload' ] ) ) {
2246
			$question_right_answer = $data[ 'add_question_right_answer_fileupload' ];
2247
		} // End If Statement
2248
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_wrong_answer_fileupload' ] ) && ( '' != $data[ 'add_question_wrong_answer_fileupload' ] ) ) {
2249
			$question_wrong_answers = array( $data[ 'add_question_wrong_answer_fileupload' ] );
2250
		} // End If Statement
2251
2252
		// Handle Question Grade
2253
		if ( isset( $data[ 'question_grade' ] ) && ( '' != $data[ 'question_grade' ] ) ) {
2254
			$question_grade = $data[ 'question_grade' ];
2255
		} // End If Statement
2256
2257
		// Handle Answer Feedback
2258
		$answer_feedback = '';
2259
		if ( isset( $data[ 'answer_feedback_boolean' ] ) && !empty( $data[ 'answer_feedback_boolean' ] ) ) {
2260
2261
            $answer_feedback = $data[ 'answer_feedback_boolean' ];
2262
2263
		}elseif( isset( $data[ 'answer_feedback_multiple_choice' ] ) && !empty( $data[ 'answer_feedback_multiple_choice' ] ) ){
2264
2265
            $answer_feedback = $data[ 'answer_feedback_multiple_choice' ];
2266
2267
        }elseif( isset( $data[ 'answer_feedback' ] )  ){
2268
2269
            $answer_feedback = $data[ 'answer_feedback' ];
2270
2271
        } // End If Statement
2272
2273
		$post_title = $question_text;
2274
		$post_author = $data[ 'post_author' ];
0 ignored issues
show
Unused Code introduced by
$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...
2275
		$post_status = 'publish';
2276
		$post_type = 'question';
2277
		// Handle the extended question text
2278
		if ( isset( $data[ 'question_description' ] ) && ( '' != $data[ 'question_description' ] ) ) {
2279
			$post_content = $data[ 'question_description' ];
2280
		}
2281
		else {
2282
			$post_content = '';
2283
		}
2284
		// Question Query Arguments
2285
		$post_type_args = array(	'post_content' => $post_content,
2286
  		    						'post_status' => $post_status,
2287
  		    						'post_title' => $post_title,
2288
  		    						'post_type' => $post_type
2289
  		    						);
2290
2291
  		// Remove empty values and reindex the array
2292
  		if ( is_array( $question_right_answers ) && 0 < count($question_right_answers) ) {
2293
  			$question_right_answers_array = array_values( array_filter( $question_right_answers, 'strlen' ) );
2294
  			$question_right_answers = array();
2295
2296
  			foreach( $question_right_answers_array as $answer ) {
2297
  				if( ! in_array( $answer, $question_right_answers ) ) {
2298
  					$question_right_answers[] = $answer;
2299
  				}
2300
  			}
2301
  			if ( 0 < count($question_right_answers) ) {
2302
  				$question_right_answer = $question_right_answers;
2303
  			}
2304
  		} // End If Statement
2305
  		$right_answer_count = count( $question_right_answer );
2306
2307
		// Remove empty values and reindex the array
2308
  		if ( is_array( $question_wrong_answers ) ) {
2309
  			$question_wrong_answers_array = array_values( array_filter( $question_wrong_answers, 'strlen' ) );
2310
  			$question_wrong_answers = array();
2311
  		} // End If Statement
2312
2313
  		foreach( $question_wrong_answers_array as $answer ) {
0 ignored issues
show
Bug introduced by
The variable $question_wrong_answers_array does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2314
  			if( ! in_array( $answer, $question_wrong_answers ) ) {
2315
  				$question_wrong_answers[] = $answer;
2316
  			}
2317
  		}
2318
2319
  		$wrong_answer_count = count( $question_wrong_answers );
2320
2321
  		// Only save if there is a valid title
2322
  		if ( $post_title != '' ) {
2323
2324
  			// Get Quiz ID for the question
2325
  		    $quiz_id = $data['quiz_id'];
2326
2327
  		    // Get question media
2328
			$question_media = $data['question_media'];
2329
2330
  		    // Get answer order
2331
  		    $answer_order = '';
2332
  		    if( isset( $data['answer_order'] ) ) {
2333
				$answer_order = $data['answer_order'];
2334
			}
2335
2336
			// Get random order selection
2337
			$random_order = 'no';
2338
			if( isset( $data['random_order'] ) ) {
2339
				$random_order = $data['random_order'];
2340
			}
2341
2342
  		    // Insert or Update the question
2343
  		    if ( 0 < $question_id ) {
2344
2345
  		    	$post_type_args[ 'ID' ] = $question_id;
2346
		    	$question_id = wp_update_post( $post_type_args );
2347
2348
		    	// Update poast meta
2349
		    	if( 'quiz' == $context ) {
2350
		    		$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2351
		    		if( ! in_array( $quiz_id, $quizzes ) ) {
2352
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2353
			    	}
2354
		    	}
2355
2356
		    	update_post_meta( $question_id, '_question_grade', $question_grade );
0 ignored issues
show
Bug introduced by
The variable $question_grade does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2357
		    	update_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2358
		    	update_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2359
		    	update_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2360
		    	update_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2361
		    	update_post_meta( $question_id, '_question_media', $question_media );
2362
		    	update_post_meta( $question_id, '_answer_order', $answer_order );
2363
		    	update_post_meta( $question_id, '_random_order', $random_order );
2364
2365
		    	if( 'quiz' != $context ) {
2366
		    		wp_set_post_terms( $question_id, array( $question_type ), 'question-type', false );
2367
		    	}
2368
				// Don't store empty value, no point
2369
				if ( !empty($answer_feedback) ) {
2370
					update_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2371
				}
2372
2373
		    } else {
2374
				$question_id = wp_insert_post( $post_type_args );
2375
				$question_count = intval( $data['question_count'] );
2376
				++$question_count;
2377
2378
				// Set post meta
2379 View Code Duplication
				if( 'quiz' == $context ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2380
					add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2381
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2382
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2383
				}
2384
2385
				if( isset( $question_grade ) ) {
2386
		    		add_post_meta( $question_id, '_question_grade', $question_grade );
2387
		    	}
2388
		    	add_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2389
		    	add_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2390
		    	add_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2391
		    	add_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2392
		    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2393
		    	add_post_meta( $question_id, '_question_media', $question_media );
2394
		    	add_post_meta( $question_id, '_answer_order', $answer_order );
2395
		    	add_post_meta( $question_id, '_random_order', $random_order );
2396
				// Don't store empty value, no point
2397
				if ( !empty($answer_feedback) ) {
2398
					add_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2399
				}
2400
2401
		    	// Set the post terms for question-type
2402
			    wp_set_post_terms( $question_id, array( $question_type ), 'question-type' );
2403
2404
			    if( $question_category ) {
2405
	    			wp_set_post_terms( $question_id, array( $question_category ), 'question-category' );
2406
	    		}
2407
2408
		    } // End If Statement
2409
		} // End If Statement
2410
  		// Check that the insert or update saved by testing the post id
2411
  		if ( 0 < $question_id ) {
2412
  			$return = $question_id;
2413
  		} // End If Statement
2414
  		return $return;
2415
  	} // End lesson_question_save()
2416
2417
2418
	/**
2419
	 * lesson_delete_question function.
2420
	 *
2421
	 * @access private
2422
	 * @param array $data (default: array())
2423
	 * @return boolean
2424
	 */
2425
	private function lesson_delete_question( $data = array() ) {
2426
2427
		// Get which question to delete
2428
		$question_id = 0;
2429 View Code Duplication
		if ( isset( $data[ 'question_id' ] ) && ( 0 < absint( $data[ 'question_id' ] ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2430
			$question_id = absint( $data[ 'question_id' ] );
2431
		} // End If Statement
2432
		// Delete the question
2433
		if ( 0 < $question_id ) {
2434
			$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2435
2436
			foreach( $quizzes as $quiz_id ) {
2437
				if( $quiz_id == $data['quiz_id'] ) {
2438
					delete_post_meta( $question_id, '_quiz_id', $quiz_id );
2439
				}
2440
			}
2441
2442
			return true;
2443
		} // End If Statement
2444
		return false;
2445
	} // End lesson_delete_question()
2446
2447
2448
	/**
2449
	 * lesson_complexities function.
2450
	 *
2451
	 * @access public
2452
	 * @return array $lesson_complexities
2453
	 */
2454
	public function lesson_complexities() {
2455
2456
		// V2 - make filter for this array
2457
        $lesson_complexities = array( 	'easy' => __( 'Easy', 'woothemes-sensei' ),
2458
									'std' => __( 'Standard', 'woothemes-sensei' ),
2459
									'hard' => __( 'Hard', 'woothemes-sensei' )
2460
									);
2461
2462
		return $lesson_complexities;
2463
2464
	} // End lesson_complexities
2465
2466
2467
	/**
2468
	 * lesson_count function.
2469
	 *
2470
	 * @access public
2471
	 * @param string $post_status (default: 'publish')
2472
	 * @return int
2473
	 */
2474
	public function lesson_count( $post_status = 'publish', $course_id = false ) {
2475
2476
		$post_args = array(	'post_type'         => 'lesson',
2477
							'posts_per_page'    => -1,
2478
//							'orderby'           => 'menu_order date',
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2479
//							'order'             => 'ASC',
2480
							'post_status'       => $post_status,
2481
							'suppress_filters'  => 0,
2482
							'fields'            => 'ids',
2483
							);
2484
		if( $course_id ) {
2485
			$post_args['meta_query'][] = array(
2486
				'key' => '_lesson_course',
2487
				'value' => $course_id,
2488
			);
2489
		}
2490
		else {
2491
			// Simple check for connection to a Course
2492
			$post_args['meta_query'][] = array(
2493
				'key' => '_lesson_course',
2494
				'value' => 0,
2495
				'compare' => '>=',
2496
			);
2497
		}
2498
2499
		// Allow WP to generate the complex final query, just shortcut to only do an overall count
2500
//		add_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
2501
		$lessons_query = new WP_Query( apply_filters( 'sensei_lesson_count', $post_args ) );
2502
//		remove_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2503
2504
		return count( $lessons_query->posts );
2505
	} // End lesson_count()
2506
2507
2508
	/**
2509
	 * lesson_quizzes function.
2510
	 *
2511
	 * @access public
2512
	 * @param int $lesson_id (default: 0)
2513
	 * @param string $post_status (default: 'publish')
2514
	 * @param string $fields (default: 'ids')
2515
	 * @return int $quiz_id
2516
	 */
2517 View Code Duplication
	public function lesson_quizzes( $lesson_id = 0, $post_status = 'any', $fields = 'ids' ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2518
2519
		$posts_array = array();
0 ignored issues
show
Unused Code introduced by
$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...
2520
2521
		$post_args = array(	'post_type' 		=> 'quiz',
2522
							'posts_per_page' 		=> 1,
2523
							'orderby'         	=> 'title',
2524
    						'order'           	=> 'DESC',
2525
    						'post_parent'      	=> $lesson_id,
2526
    						'post_status'		=> $post_status,
2527
							'suppress_filters' 	=> 0,
2528
							'fields'            => $fields
2529
							);
2530
		$posts_array = get_posts( $post_args );
2531
        $quiz_id = array_shift($posts_array);
2532
2533
		return $quiz_id;
2534
	} // End lesson_quizzes()
2535
2536
2537
	/**
2538
	 * Fetches all the questions for a quiz depending on certain conditions.
2539
     *
2540
     * Determine which questions should be shown depending on:
2541
     * - admin/teacher selected questions to be shown
2542
     * - questions shown to a user previously (saved as asked questions)
2543
     * - limit number of questions lesson setting
2544
	 *
2545
     * @since 1.0
2546
	 * @param int $quiz_id (default: 0)
2547
	 * @param string $post_status (default: 'publish')
2548
	 * @param string $orderby (default: 'meta_value_num title')
2549
	 * @param string $order (default: 'ASC')
2550
     *
2551
	 * @return array $questions { $question type WP_Post }
2552
	 */
2553
	public function lesson_quiz_questions( $quiz_id = 0, $post_status = 'any', $orderby = 'meta_value_num title', $order = 'ASC' ) {
2554
2555
		$quiz_id = (string) $quiz_id;
2556
        $quiz_lesson_id = Sensei()->quiz->get_lesson_id( $quiz_id );
2557
2558
        // setup the user id
2559
        if( is_admin() ) {
2560
            $user_id = isset( $_GET['user'] ) ? $_GET['user'] : '' ;
2561
        } else {
2562
            $user_id = get_current_user_id();
2563
        }
2564
2565
        // get the users current status on the lesson
2566
        $user_lesson_status = Sensei_Utils::user_lesson_status( $quiz_lesson_id, $user_id );
2567
2568
		// Set the default question order if it has not already been set for this quiz
2569
		$this->set_default_question_order( $quiz_id );
2570
2571
		// If viewing quiz on the frontend then show questions in random order if set
2572
		if ( ! is_admin() ) {
2573
			$random_order = get_post_meta( $quiz_id, '_random_question_order', true );
2574
			if( $random_order && $random_order == 'yes' ) {
2575
				$orderby = 'rand';
2576
			}
2577
		}
2578
2579
		// Get all questions and multiple questions
2580
		$question_query_args = array(
2581
			'post_type' 		=> array( 'question', 'multiple_question' ),
2582
			'posts_per_page' 	=> -1,
2583
			'meta_key'        	=> '_quiz_question_order' . $quiz_id,
2584
			'orderby'         	=> $orderby,
2585
			'order'           	=> $order,
2586
			'meta_query'		=> array(
2587
				array(
2588
					'key'       => '_quiz_id',
2589
					'value'     => $quiz_id,
2590
				)
2591
			),
2592
			'post_status'		=> $post_status,
2593
			'suppress_filters' 	=> 0
2594
		);
2595
2596
        //query the questions
2597
		$questions_query = new WP_Query( $question_query_args );
2598
2599
        // Set return array to initially include all items
2600
        $questions = $questions_query->posts;
2601
2602
        // set the questions array that will be manipulated within this function
2603
        $questions_array = $questions_query->posts;
2604
2605
		// If viewing quiz on frontend or in grading then only single questions must be shown
2606
		$selected_questions = false;
2607
		if( ! is_admin() || ( is_admin() && isset( $_GET['page'] ) && 'sensei_grading' == $_GET['page'] && isset( $_GET['user'] ) && isset( $_GET['quiz_id'] ) ) ) {
2608
2609
			// Fetch the questions that the user was asked in their quiz if they have already completed it
2610
			$questions_asked_string = !empty( $user_lesson_status->comment_ID) ? get_comment_meta( $user_lesson_status->comment_ID, 'questions_asked', true ) : false;
2611
			if( !empty($questions_asked_string) ) {
2612
2613
				$selected_questions = explode( ',', $questions_asked_string );
2614
2615
				// Fetch each question in the order in which they were asked
2616
				$questions = array();
2617
				foreach( $selected_questions as $question_id ) {
2618
					if( ! $question_id ) continue;
2619
					$question = get_post( $question_id );
2620
					if( ! isset( $question ) || ! isset( $question->ID ) ) continue;
2621
					$questions[] = $question;
2622
				}
2623
2624
			} else {
2625
2626
				// Otherwise, make sure that we convert all multiple questions into single questions
2627
2628
				$multiple_array = array();
0 ignored issues
show
Unused Code introduced by
$multiple_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...
2629
				$existing_questions = array();
2630
2631
				// Set array of questions that already exist so we can prevent duplicates from appearing
2632
				foreach( $questions_array as $question ) {
2633
					if( 'question' != $question->post_type ) continue;
2634
					$existing_questions[] = $question->ID;
2635
				}
2636
2637
				// Include only single questions in the return array
2638
				$questions_loop = $questions_array;
2639
				$questions_array = array();
2640
				foreach( $questions_loop as $k => $question ) {
2641
2642
					// If this is a single question then include it
2643
					if( 'question' == $question->post_type ) {
2644
						$questions_array[] = $question;
2645
					} else {
2646
2647
						// If this is a multiple question then get the specified amount of questions from the specified category
2648
						$question_cat = intval( get_post_meta( $question->ID, 'category', true ) );
2649
						$question_number = intval( get_post_meta( $question->ID, 'number', true ) );
2650
2651
						$qargs = array(
2652
							'post_type' 		=> 'question',
2653
							'posts_per_page' 		=> $question_number,
2654
							'orderby'         	=> $orderby,
2655
							'tax_query'			=> array(
2656
								array(
2657
									'taxonomy'  => 'question-category',
2658
									'field'     => 'term_id',
2659
									'terms'		=> $question_cat
2660
								)
2661
							),
2662
							'post_status'		=> $post_status,
2663
							'suppress_filters' 	=> 0,
2664
							'post__not_in'		=> $existing_questions,
2665
						);
2666
						$cat_questions = get_posts( $qargs );
2667
2668
						// Merge results into return array
2669
						$questions_array = array_merge( $questions_array, $cat_questions );
2670
2671
						// Add selected questions to existing questions array to prevent duplicates from being added
2672
						foreach( $questions_array as $cat_question ) {
2673
							if( in_array( $cat_question->ID, $existing_questions ) ) continue;
2674
							$existing_questions[] = $cat_question->ID;
2675
						}
2676
					}
2677
				}
2678
2679
				// Set return data
2680
				$questions = $questions_array;
2681
			}
2682
		}
2683
2684
		// 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
2685
		if( ! $selected_questions ) {
2686
2687
			// Only limit questions like this on the frontend
2688
			if( ! is_admin() ) {
2689
2690
				// Get number of questions to show
2691
				$show_questions = intval( get_post_meta( $quiz_id, '_show_questions', true ) );
2692
				if( $show_questions ) {
2693
2694
					// Get random set of array keys from selected questions array
2695
					$selected_questions = array_rand( $questions_array, $show_questions );
2696
2697
					// Loop through all questions and pick the the ones to be shown based on the random key selection
2698
					$questions = array();
2699
					foreach( $questions_array as $k => $question ) {
2700
2701
						// Random keys will always be an array, unless only one question is to be shown
2702
						if( is_array( $selected_questions ) ) {
2703
							if( in_array( $k, $selected_questions ) ) {
2704
								$questions[] = $question;
2705
							}
2706
						} elseif( 1 == $show_questions ) {
2707
							if ( $selected_questions == $k ) {
2708
								$questions[] = $question;
2709
							}
2710
						}
2711
					}
2712
				}
2713
			}
2714
		}
2715
2716
        // Save the questions that will be asked for the current user
2717
        // this happens only once per user/quiz, unless the user resets the quiz
2718
        if( ! is_admin() ){
2719
2720
            if( $user_lesson_status ) {
2721
2722
                $questions_asked = get_comment_meta($user_lesson_status->comment_ID, 'questions_asked', true);
2723
                if ( empty($questions_asked) && $user_lesson_status) {
2724
2725
                    $questions_asked = array();
2726
                    foreach ($questions as $question) {
2727
2728
                        $questions_asked[] = $question->ID;
2729
2730
                    }
2731
2732
                    // save the questions asked id
2733
                    $questions_asked_csv = implode(',', $questions_asked);
2734
                    update_comment_meta($user_lesson_status->comment_ID, 'questions_asked', $questions_asked_csv);
2735
2736
                }
2737
            }
2738
        }
2739
2740
        /**
2741
         * Filter the questions returned by Sensei_Lesson::lessons_quiz_questions
2742
         *
2743
         * @hooked Sensei_Teacher::allow_teacher_access_to_questions
2744
         * @since 1.8.0
2745
         */
2746
		return apply_filters( 'sensei_lesson_quiz_questions', $questions,  $quiz_id  );
2747
2748
	} // End lesson_quiz_questions()
2749
2750
	/**
2751
	 * Set the default quiz order
2752
	 * @param integer $quiz_id ID of quiz
2753
	 */
2754
	public function set_default_question_order( $quiz_id = 0 ) {
2755
2756
		if( $quiz_id ) {
2757
2758
			$question_order = get_post_meta( $quiz_id, '_question_order', true );
2759
2760
			if( ! $question_order ) {
2761
2762
				$args = array(
2763
					'post_type' 		=> 'question',
2764
					'posts_per_page' 		=> -1,
2765
					'orderby'         	=> 'ID',
2766
					'order'           	=> 'ASC',
2767
					'meta_query'		=> array(
2768
						array(
2769
							'key'       => '_quiz_id',
2770
							'value'     => $quiz_id
2771
						)
2772
					),
2773
					'post_status'		=> 'any',
2774
					'suppress_filters' 	=> 0
2775
				);
2776
				$questions = get_posts( $args );
2777
2778
				$o = 1;
2779
				foreach( $questions as $question ) {
2780
					add_post_meta( $question->ID, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $o, true );
2781
					$o++;
2782
				}
2783
			}
2784
		}
2785
2786
	}
2787
2788
	/**
2789
	 * lesson_image function.
2790
	 *
2791
	 * Handles output of the lesson image
2792
	 *
2793
	 * @access public
2794
	 * @param int $lesson_id (default: 0)
2795
	 * @param string $width (default: '100')
2796
	 * @param string $height (default: '100')
2797
	 * @return string
2798
	 */
2799
	public function lesson_image( $lesson_id = 0, $width = '100', $height = '100', $widget = false ) {
2800
2801
		$html = '';
2802
2803
		// Get Width and Height settings
2804
		if ( ( $width == '100' ) && ( $height == '100' ) ) {
2805
2806
			if ( is_singular( 'lesson' ) ) {
2807
2808
				if ( ! $widget && ! Sensei()->settings->settings[ 'lesson_single_image_enable' ] ) {
2809
2810
					return '';
2811
2812
				} // End If Statement
2813
2814
				$image_thumb_size = 'lesson_single_image';
2815
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2816
				$width = $dimensions['width'];
2817
				$height = $dimensions['height'];
2818
				$crop = $dimensions['crop'];
0 ignored issues
show
Unused Code introduced by
$crop 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...
2819
2820
			} else {
2821
2822
				if ( ! $widget && ! Sensei()->settings->settings[ 'course_lesson_image_enable' ] ) {
2823
2824
					return '';
2825
				} // End If Statement
2826
2827
				$image_thumb_size = 'lesson_archive_image';
2828
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2829
				$width = $dimensions['width'];
2830
				$height = $dimensions['height'];
2831
				$crop = $dimensions['crop'];
0 ignored issues
show
Unused Code introduced by
$crop 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...
2832
2833
			} // End If Statement
2834
2835
		} // End If Statement
2836
2837
		$img_url = '';
2838
2839
		if ( has_post_thumbnail( $lesson_id ) ) {
2840
2841
   			// Get Featured Image
2842
   			$img_url = get_the_post_thumbnail( $lesson_id, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
2843
2844 View Code Duplication
 		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2845
2846
 			// Display Image Placeholder if none
2847
			if ( Sensei()->settings->settings[ 'placeholder_images_enable' ] ) {
2848
2849
                $img_url = apply_filters( 'sensei_lesson_placeholder_image_url', '<img src="http://placehold.it/' . $width . 'x' . $height . '" class="woo-image thumbnail alignleft" />' );
2850
2851
			} // End If Statement
2852
2853
		} // End If Statement
2854
2855
		$html .= '<a href="' . get_permalink( $lesson_id ) . '" title="' . esc_attr( get_post_field( 'post_title', $lesson_id ) ) . '">' . $img_url . '</a>';
2856
2857
		return $html;
2858
2859
	} // End lesson_image()
2860
2861
    /**
2862
     * Ooutpu the lesson image
2863
     *
2864
     * @since 1.9.0
2865
     * @param integer $lesson_id
2866
     */
2867
    public static function the_lesson_image( $lesson_id = 0 ){
2868
2869
        echo Sensei()->lesson->lesson_image( $lesson_id );
2870
2871
    }
2872
2873
	/**
2874
	 * Returns the the lesson excerpt.
2875
	 *
2876
	 * @param WP_Post $lesson
2877
     * @param bool $add_p_tags should the excerpt be wrapped by calling wpautop()
2878
	 * @return string
2879
	 */
2880
	public static function lesson_excerpt( $lesson = null, $add_p_tags = true ) {
2881
		$html = '';
2882
		if ( is_a( $lesson, 'WP_Post' ) && 'lesson' == $lesson->post_type ) {
2883
2884
            $excerpt =  $lesson->post_excerpt;
2885
2886
            // if $add_p_tags true wrap with <p> else return the excerpt as is
2887
            $html =  $add_p_tags ? wpautop( $excerpt ) : $excerpt;
2888
2889
		}
2890
		return apply_filters( 'sensei_lesson_excerpt', $html );
2891
2892
	} // End lesson_excerpt()
2893
2894
    /**
2895
     * Returns the course for a given lesson
2896
     *
2897
     * @since 1.7.4
2898
     * @access public
2899
     *
2900
     * @param int $lesson_id
2901
     * @return int|bool $course_id or bool when nothing is found.
2902
     */
2903
     public function get_course_id( $lesson_id ){
2904
2905
         if( ! isset( $lesson_id ) || empty( $lesson_id )
2906
         ||  'lesson' != get_post_type( $lesson_id ) ){
2907
             return false;
2908
         }
2909
2910
         $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true);
2911
2912
         // make sure the course id is valid
2913
         if( empty( $lesson_course_id )
2914
             || is_array( $lesson_course_id )
2915
             || intval( $lesson_course_id ) < 1
2916
             || 'course' != get_post_type( $lesson_course_id ) ){
2917
2918
             return false;
2919
2920
         }
2921
2922
         return $lesson_course_id;
2923
2924
     }// en get_course_id
2925
2926
    /**
2927
     * Add the admin all lessons screen edit options.
2928
     *
2929
     * The fields in this function work for both quick and bulk edit. The ID attributes is used
2930
     * by bulk edit javascript in the front end to retrieve the new values set byt the user. Then
2931
     * name attribute is will be used by the quick edit and submitted via standard POST. This
2932
     * will use this classes save_post_meta function to save the new field data.
2933
     *
2934
     * @hooked quick_edit_custom_box
2935
     * @hooked bulk_edit_custom_box
2936
     *
2937
     * @since 1.8.0
2938
     *
2939
     * @param string $column_name
2940
     * @param string $post_type
2941
     * @return void
2942
     */
2943
    public function all_lessons_edit_fields( $column_name, $post_type ) {
2944
2945
        // only show these options ont he lesson post type edit screen
2946
        if( 'lesson' != $post_type || 'lesson-course' != $column_name ){
2947
            return;
2948
        }
2949
2950
        ?>
2951
        <fieldset class="sensei-edit-field-set inline-edit-lesson">
2952
            <div class="sensei-inline-edit-col column-<?php echo $column_name ?>">
2953
                    <?php
2954
                    echo '<h4>' . __('Lesson Information', 'woothemes-sensei') . '</h4>';
2955
                    // create a nonce field to be  used as a security measure when saving the data
2956
                    wp_nonce_field( 'bulk-edit-lessons', '_edit_lessons_nonce' );
2957
                    wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce'  );
2958
2959
                    // unchanged option - we need this in because
2960
                    // the default option in bulk edit should not be empty. If it is
2961
                    // the user will erase data they didn't want to touch.
2962
                    $no_change_text = '-- ' . __('No Change', 'woothemes-sensei') . ' --';
2963
2964
                    //
2965
                    //course selection
2966
                    //
2967
                    $courses =  WooThemes_Sensei_Course::get_all_courses();
2968
                    $course_options = array();
2969
                    if ( count( $courses ) > 0 ) {
2970
                        foreach ($courses as $course ){
2971
                            $course_options[ $course->ID ] = get_the_title( $course->ID );
2972
                        }
2973
                    }
2974
                    //pre-append the no change option
2975
                    $course_options['-1']=  $no_change_text;
2976
                    $course_attributes = array( 'name'=> 'lesson_course', 'id'=>'sensei-edit-lesson-course' , 'class'=>' ' );
2977
                    $course_field =  Sensei_Utils::generate_drop_down( '-1', $course_options, $course_attributes );
2978
                    echo $this->generate_all_lessons_edit_field( __('Lesson Course', 'woothemes-sensei'),   $course_field  );
2979
2980
                    //
2981
                    // lesson complexity selection
2982
                    //
2983
                    $lesson_complexities =  $this->lesson_complexities();
2984
                    //pre-append the no change option
2985
                    $lesson_complexities['-1']=  $no_change_text;
2986
                    $complexity_dropdown_attributes = array( 'name'=> 'lesson_complexity', 'id'=>'sensei-edit-lesson-complexity' , 'class'=>' ');
2987
                    $complexity_filed =  Sensei_Utils::generate_drop_down( '-1', $lesson_complexities, $complexity_dropdown_attributes );
2988
                    echo $this->generate_all_lessons_edit_field( __('Lesson Complexity', 'woothemes-sensei'),   $complexity_filed  );
2989
2990
                    ?>
2991
2992
                    <h4><?php _e('Quiz Settings', 'woothemes-sensei'); ?> </h4>
2993
2994
                    <?php
2995
2996
                    //
2997
                    // Lesson require pass to complete
2998
                    //
2999
                    $pass_required_options = array(
3000
                        '-1' => $no_change_text,
3001
                         '0' => __('No','woothemes'),
3002
                         '1' => __('Yes','woothemes'),
3003
                    );
3004
3005
                    $pass_required_select_attributes = array( 'name'=> 'pass_required',
3006
                                                                'id'=> 'sensei-edit-lesson-pass-required',
3007
                                                                'class'=>' '   );
3008
                    $require_pass_field =  Sensei_Utils::generate_drop_down( '-1', $pass_required_options, $pass_required_select_attributes, false );
3009
                    echo $this->generate_all_lessons_edit_field( __('Pass required', 'woothemes-sensei'),   $require_pass_field  );
3010
3011
                    //
3012
                    // Quiz pass percentage
3013
                    //
3014
                    $quiz_pass_percentage_field = '<input name="quiz_passmark" id="sensei-edit-quiz-pass-percentage" type="number" />';
3015
                    echo $this->generate_all_lessons_edit_field( __('Pass Percentage', 'woothemes-sensei'), $quiz_pass_percentage_field  );
3016
3017
                    //
3018
                    // Enable quiz reset button
3019
                    //
3020
                    $quiz_reset_select__options = array(
3021
                        '-1' => $no_change_text,
3022
                        '0' => __('No','woothemes'),
3023
                        '1' => __('Yes','woothemes'),
3024
                    );
3025
                    $quiz_reset_name_id = 'sensei-edit-enable-quiz-reset';
3026
                    $quiz_reset_select_attributes = array( 'name'=> 'enable_quiz_reset', 'id'=>$quiz_reset_name_id, 'class'=>' ' );
3027
                    $quiz_reset_field =  Sensei_Utils::generate_drop_down( '-1', $quiz_reset_select__options, $quiz_reset_select_attributes, false );
3028
                    echo $this->generate_all_lessons_edit_field( __('Enable quiz reset button', 'woothemes-sensei'), $quiz_reset_field  );
3029
3030
                    ?>
3031
            </div>
3032
        </fieldset>
3033
    <?php
3034
    }// all_lessons_edit_fields
3035
3036
    /**
3037
     * Create the html for the edit field
3038
     *
3039
     * Wraps the passed in field and title combination with the correct html.
3040
     *
3041
     * @since 1.8.0
3042
     *
3043
     * @param string $title that will stand to the left of the field.
3044
     * @param string $field type markup for the field that must be wrapped.
3045
     * @return string $field_html
3046
     */
3047
    public function generate_all_lessons_edit_field( $title  ,$field ){
3048
3049
        $html = '';
0 ignored issues
show
Unused Code introduced by
$html 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...
3050
        $html = '<div class="inline-edit-group" >';
3051
        $html .=  '<span class="title">'. $title .'</span> ';
3052
        $html .= '<span class="input-text-wrap">';
3053
        $html .= $field;
3054
        $html .= '</span>';
3055
        $html .= '</label></div>';
3056
3057
        return $html ;
3058
3059
    }//end generate_all_lessons_edit_field
3060
3061
    /**
3062
     * Respond to the ajax call from the bulk edit save function. This comes
3063
     * from the admin all lesson screen.
3064
     *
3065
     * @since 1.8.0
3066
     * @return void
3067
     */
3068
    function save_all_lessons_edit_fields() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3069
3070
        // verify all the data before attempting to save
3071
        if( ! isset( $_POST['security'] ) || ! check_ajax_referer( 'bulk-edit-lessons', 'security' )
3072
            ||  empty( $_POST[ 'post_ids' ] )  || ! is_array( $_POST[ 'post_ids' ] ) ) {
3073
            die();
3074
        }
3075
3076
        // get our variables
3077
        $new_course = sanitize_text_field(  $_POST['sensei_edit_lesson_course'] );
3078
        $new_complexity = sanitize_text_field(  $_POST['sensei_edit_complexity'] );
3079
        $new_pass_required = sanitize_text_field(  $_POST['sensei_edit_pass_required'] );
3080
        $new_pass_percentage = sanitize_text_field(  $_POST['sensei_edit_pass_percentage'] );
3081
        $new_enable_quiz_reset = sanitize_text_field(  $_POST['sensei_edit_enable_quiz_reset'] );
3082
        // store the values for all selected posts
3083
        foreach( $_POST[ 'post_ids' ] as $lesson_id ) {
3084
3085
            // get the quiz id needed for the quiz meta
3086
            $quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
3087
3088
            // do not save the items if the value is -1 as this
3089
            // means it was not changed
3090
3091
            // update lesson course
3092
            if( -1 != $new_course ){
3093
                update_post_meta( $lesson_id, '_lesson_course', $new_course );
3094
            }
3095
            // update lesson complexity
3096
            if( -1 != $new_complexity ){
3097
                update_post_meta( $lesson_id, '_lesson_complexity', $new_complexity );
3098
            }
3099
3100
            // Quiz Related settings
3101
            if( isset( $quiz_id) && 0 < intval( $quiz_id ) ) {
3102
3103
                // update pass required
3104
                if (-1 != $new_pass_required) {
3105
3106
                    $checked = $new_pass_required  ? 'on' : '';
3107
                    update_post_meta($quiz_id, '_pass_required', $checked);
3108
                    unset( $checked );
3109
                }
3110
3111
                // update pass percentage
3112
                if( !empty( $new_pass_percentage) && is_numeric( $new_pass_percentage ) ){
3113
3114
                        update_post_meta($quiz_id, '_quiz_passmark', $new_pass_percentage);
3115
3116
                }
3117
3118
                //
3119
                // update enable quiz reset
3120
                //
3121
                if (-1 != $new_enable_quiz_reset ) {
3122
3123
                    $checked = $new_enable_quiz_reset ? 'on' : ''  ;
3124
                    update_post_meta($quiz_id, '_enable_quiz_reset', $checked);
3125
                    unset( $checked );
3126
3127
                }
3128
3129
3130
            } // end if quiz
3131
3132
        }// end for each
3133
3134
        die();
3135
3136
    } // end save_all_lessons_edit_fields
3137
3138
    /**
3139
     * Loading the quick edit fields defaults.
3140
     *
3141
     * This function will localise the default values along with the script that will
3142
     * add these values to the inputs.
3143
     *
3144
     * NOTE: this function runs for each row in the edit column
3145
     *
3146
     * @since 1.8.0
3147
     * @return void
3148
     */
3149
    public function set_quick_edit_admin_defaults( $column_name, $post_id ){
3150
3151
        if( 'lesson-course' != $column_name ){
3152
            return;
3153
        }
3154
        // load the script
3155
        $suffix = defined( 'SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
3156
        wp_enqueue_script( 'sensei-lesson-quick-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-quick-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
3157
3158
        // setup the values for all meta fields
3159
        $data = array();
3160
        foreach( $this->meta_fields as $field ){
3161
3162
            $data[$field] =  get_post_meta( $post_id, '_'.$field, true );
3163
3164
        }
3165
        // add quiz meta fields
3166
        $quiz_id = Sensei()->lesson->lesson_quizzes( $post_id );
3167
        foreach( Sensei()->quiz->meta_fields as $field ){
3168
3169
            $data[$field] =  get_post_meta( $quiz_id, '_'.$field, true );
3170
3171
        }
3172
3173
        wp_localize_script( 'sensei-lesson-quick-edit', 'sensei_quick_edit_'.$post_id, $data );
3174
3175
    }// end quick edit admin defaults
3176
3177
    /**
3178
     * Filter the classes for lessons on the single course page.
3179
     *
3180
     * Adds the nesecary classes depending on the user data
3181
     *
3182
     * @since 1.9.0
3183
     * @param array $classes
3184
     * @return array $classes
3185
     */
3186
    public static function single_course_lessons_classes( $classes ){
3187
3188
        if(  is_singular('course') ){
3189
3190
            global $post;
3191
            $course_id = $post->ID;
3192
3193
            $lesson_classes = array( 'course', 'post' );
3194
            if ( is_user_logged_in() ) {
3195
3196
                // Check if Lesson is complete
3197
                $single_lesson_complete = Sensei_Utils::user_completed_lesson( get_the_ID(), get_current_user_id() );
3198
                if ( $single_lesson_complete ) {
3199
3200
                    $lesson_classes[] = 'lesson-completed';
3201
3202
                } // End If Statement
3203
3204
            } // End If Statement
3205
3206
            $is_user_taking_course = Sensei_Utils::user_started_course( $course_id, get_current_user_id() );
3207
            if (  Sensei_Utils::is_preview_lesson( get_the_ID() ) && !$is_user_taking_course ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_user_taking_course of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3208
3209
                $lesson_classes[] = 'lesson-preview';
3210
3211
            }
3212
3213
            $classes = array_merge( $classes, $lesson_classes  );
3214
3215
        }
3216
3217
        return $classes;
3218
3219
    }// end single_course_lessons_classes
3220
3221
    /**
3222
     * Output the lesson meta for the given lesson
3223
     *
3224
     * @since 1.9.0
3225
     * @param $lesson_id
3226
     */
3227
    public static function the_lesson_meta( $lesson_id ){
3228
3229
        global $wp_query;
3230
        $loop_lesson_number = $wp_query->current_post + 1;
3231
3232
        $course_id = Sensei()->lesson->get_course_id( $lesson_id );
3233
        $single_lesson_complete = false;
3234
        $is_user_taking_course = Sensei_Utils::user_started_course( $course_id, get_current_user_id() );
0 ignored issues
show
Bug introduced by
It seems like $course_id defined by Sensei()->lesson->get_course_id($lesson_id) on line 3232 can also be of type boolean; however, Sensei_Utils::user_started_course() does only seem to accept integer, 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...
3235
3236
        // Get Lesson data
3237
        $complexity_array = Sensei()->lesson->lesson_complexities();
3238
3239
        $lesson_complexity = get_post_meta( $lesson_id, '_lesson_complexity', true );
3240
        if ( '' != $lesson_complexity ) {
3241
3242
            $lesson_complexity = $complexity_array[$lesson_complexity];
3243
3244
        }
3245
        $user_info = get_userdata( absint( get_post()->post_author ) );
3246
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id);
3247
        $preview_label = '';
3248
        if ( $is_preview && !$is_user_taking_course ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_user_taking_course of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3249
3250
            $preview_label = Sensei()->frontend->sensei_lesson_preview_title_text( $lesson_id);
3251
            $preview_label = '<span class="preview-heading">' . $preview_label . '</span>';
3252
3253
        }
3254
3255
3256
        $count_markup= '';
3257
        /**
3258
         * Filter for if you want the $lesson_count to show next to the lesson.
3259
         *
3260
         * @since 1.0
3261
         * @param bool default false.
3262
         */
3263
        if( apply_filters( 'sensei_show_lesson_numbers', false ) ) {
3264
3265
            $count_markup =  '<span class="lesson-number">' . $loop_lesson_number. '</span>';
3266
3267
        }
3268
3269
        $heading_link_title = sprintf( __( 'Start %s', 'woothemes-sensei' ), get_the_title( $lesson_id ) );
3270
3271
        ?>
3272
        <header>
3273
            <h2>
3274
                <a href="<?php echo esc_url_raw( get_permalink( $lesson_id ) ) ?>"
3275
                   title="<?php esc_attr_e( $heading_link_title ) ?>" >
3276
                    <?php echo $count_markup. get_the_title( $lesson_id ) . $preview_label; ?>
3277
                </a>
3278
            </h2>
3279
3280
            <p class="lesson-meta">
3281
3282
                <?php
3283
3284
                $meta_html = '';
3285
                $user_lesson_status = Sensei_Utils::user_lesson_status( get_the_ID(), get_current_user_id() );
3286
3287
                $lesson_length = get_post_meta( $lesson_id, '_lesson_length', true );
3288 View Code Duplication
                if ( '' != $lesson_length ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3289
3290
                    $meta_html .= '<span class="lesson-length">' .  __( 'Length: ', 'woothemes-sensei' ) . $lesson_length . __( ' minutes', 'woothemes-sensei' ) . '</span>';
3291
3292
                }
3293
3294
                if ( Sensei()->settings->get( 'lesson_author' ) ) {
3295
3296
                    $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>';
3297
3298
                } // End If Statement
3299
                if ( '' != $lesson_complexity ) {
3300
3301
                    $meta_html .= '<span class="lesson-complexity">' .  __( 'Complexity: ', 'woothemes-sensei' ) . $lesson_complexity .'</span>';
3302
3303
                }
3304
3305 View Code Duplication
                if ( $single_lesson_complete ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3306
3307
                    $meta_html .= '<span class="lesson-status complete">' .__( 'Complete', 'woothemes-sensei' ) .'</span>';
3308
3309
                } elseif ( $user_lesson_status ) {
3310
3311
                    $meta_html .= '<span class="lesson-status in-progress">' . __( 'In Progress', 'woothemes-sensei' ) .'</span>';
3312
3313
                } // End If Statement
3314
3315
                echo $meta_html;
3316
3317
                ?>
3318
3319
            </p> <!-- lesson meta -->
3320
3321
        </header>
3322
3323
    <?php
3324
3325
    } // end the_lesson_meta
3326
3327
    /**
3328
     * Output the lessons thumbnail
3329
     *
3330
     * 1.9.0
3331
     *
3332
     * @param $lesson_id
3333
     */
3334
    public static function the_lesson_thumbnail( $lesson_id ){
3335
3336
        if( empty( $lesson_id ) ){
3337
3338
            $lesson_id = get_the_ID();
3339
3340
        }
3341
3342
        if( 'lesson' != get_post_type( $lesson_id ) ){
3343
            return;
3344
        }
3345
3346
        echo Sensei()->lesson->lesson_image( $lesson_id );
3347
    }
3348
3349
3350
    /**
3351
     * Alter the sensei lesson excerpt.
3352
     *
3353
     * @since 1.9.0
3354
     * @param string $excerpt
3355
     * @return string $excerpt
3356
     */
3357
    public static function alter_the_lesson_excerpt( $excerpt ) {
3358
3359
        if ('lesson' == get_post_type(get_the_ID())){
3360
3361
            // remove this hooks to avoid an infinite loop.
3362
            remove_filter( 'get_the_excerpt', array( 'WooThemes_Sensei_Lesson','alter_the_lesson_excerpt') );
3363
3364
            return WooThemes_Sensei_Lesson::lesson_excerpt( get_post( get_the_ID() ) );
3365
        }
3366
3367
        return $excerpt;
3368
3369
    }// end the_lesson_excerpt
3370
3371
    /**
3372
     * Returns the lesson prerequisite for the given lesson id.
3373
     *
3374
     * @since 1.9.0
3375
     *
3376
     * @param $current_lesson_id
3377
     * @return mixed | bool | int $prerequisite_lesson_id or false
3378
     */
3379
    public static function get_lesson_prerequisite_id( $current_lesson_id  ){
3380
3381
        $prerequisite_lesson_id = get_post_meta( $current_lesson_id , '_lesson_prerequisite', true );
3382
3383
        // set ti to false if not a valid prerequisite lesson id
3384
        if(  empty( $prerequisite_lesson_id )
3385
            || 'lesson' != get_post_type( $prerequisite_lesson_id )
3386
            || $prerequisite_lesson_id == $current_lesson_id  ) {
3387
3388
            $prerequisite_lesson_id = false;
3389
3390
        }
3391
3392
        return apply_filters( 'sensei_lesson_prerequisite', $prerequisite_lesson_id, $current_lesson_id );
3393
3394
    }
3395
3396
    /**
3397
     * This function requires that you pass in the lesson you would like to check for
3398
     * a pre-requisite and not the pre-requisite. It will check if the
3399
     * lesson has a pre-requiste and then check if it is completed.
3400
     *
3401
     * @since 1.9.0
3402
     *
3403
     * @param $lesson_id
3404
     * @param $user_id
3405
     * @return bool
3406
     */
3407
    public  static function is_prerequisite_complete( $lesson_id, $user_id  ){
3408
3409 View Code Duplication
        if( empty( $lesson_id ) || empty( $user_id )
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3410
        || 'lesson' != get_post_type( $lesson_id )
3411
        ||  ! is_a( get_user_by( 'id', $user_id ), 'WP_User' )){
3412
3413
            return false;
3414
3415
        }
3416
3417
        $pre_requisite_id = (string) self::get_lesson_prerequisite_id( $lesson_id );
3418
3419
        // not a valid pre-requisite so pre-requisite is completed
3420
        if( 'lesson' != get_post_type( $pre_requisite_id )
3421
            || ! is_numeric( $pre_requisite_id ) ){
3422
3423
            return true;
3424
3425
        }
3426
3427
        return  Sensei_Utils::user_completed_lesson( $pre_requisite_id, $user_id );
3428
3429
    }// end is_prerequisite_complete
3430
3431
    /**
3432
     * Show the user not taking course message if it is the case
3433
     *
3434
     * @since 1.9.0
3435
     */
3436
    public  static function user_not_taking_course_message(){
3437
3438
        $lesson_id = get_the_ID();
3439
3440
        if( 'lesson' != get_post_type( $lesson_id ) ){
3441
            return;
3442
        }
3443
3444
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id );
3445
        $pre_requisite_complete = self::is_prerequisite_complete( $lesson_id , get_current_user_id() );
3446
        $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true );
3447
        $user_taking_course = Sensei_Utils::user_started_course( $lesson_course_id, get_current_user_id() );
3448
3449
        if ( $pre_requisite_complete && $is_preview && !$user_taking_course ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_taking_course of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3450
            ?>
3451
3452
            <div class="sensei-message alert">
3453
                <?php echo Sensei()->permissions_message['message']; ?>
3454
            </div>
3455
3456
            <?php
3457
3458
        }// end if
3459
3460
    } // end user_not_taking_course_message
3461
3462
    /**
3463
     * Outputs the lessons course signup lingk
3464
     *
3465
     * This hook runs inside the single lesson page.
3466
     *
3467
     * @since 1.9.0
3468
     */
3469
    public static function course_signup_link( ){
3470
3471
        $course_id =  Sensei()->lesson->get_course_id( get_the_ID() );
3472
3473
        if ( empty( $course_id ) || 'course' != get_post_type( $course_id ) || sensei_all_access() ) {
3474
3475
            return;
3476
3477
        }
3478
        ?>
3479
3480
        <section class="course-signup lesson-meta">
3481
3482
            <?php
3483
            $wc_post_id = (int) get_post_meta( $course_id, '_course_woocommerce_product', true );
3484
3485
            if ( Sensei_WC::is_woocommerce_active() && ( 0 < $wc_post_id ) ) {
3486
3487
                global $current_user;
3488
                if( is_user_logged_in() ) {
3489
                    wp_get_current_user();
3490
3491
                    $course_purchased = Sensei_Utils::sensei_customer_bought_product( $current_user->user_email, $current_user->ID, $wc_post_id );
0 ignored issues
show
Deprecated Code introduced by
The method Sensei_Utils::sensei_customer_bought_product() has been deprecated with message: since 1.9.0 use Sensei_WC::has_customer_bought_product($user_id, $product_id)

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

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

Loading history...
3492
3493
                    if( $course_purchased ) {
3494
3495
                        $prereq_course_id = get_post_meta( $course_id, '_course_prerequisite',true );
3496
                        $course_link = '<a href="' . esc_url( get_permalink( $prereq_course_id ) ) . '" title="' . esc_attr( get_the_title( $prereq_course_id ) ) . '">' . __( 'the previous course', 'woothemes-sensei' )  . '</a>';
3497
                        ?>
3498
                            <div class="sensei-message info">
3499
3500
                                <?php  echo sprintf( __( 'Please complete %1$s before starting the lesson.', 'woothemes-sensei' ), $course_link ); ?>
3501
3502
                            </div>
3503
3504 View Code Duplication
                    <?php } else { ?>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3505
3506
                        <div class="sensei-message info">
3507
3508
                            <?php
3509
                            $course_link = '<a href="' . esc_url( get_permalink( $course_id ) )
3510
                                            . '"title="' . __( 'Sign Up', 'woothemes-sensei' )
3511
                                            . '">' . __( 'course', 'woothemes-sensei' )
3512
                                            . '</a>';
3513
3514
                            echo  sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $course_link );
3515
3516
                            ?>
3517
3518
                        </div>
3519
                    <?php } ?>
3520
3521 View Code Duplication
                <?php } else { ?>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3522
3523
                    <div class="sensei-message info"><?php echo sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), '<a href="' . esc_url( get_permalink( $course_id ) ) . '" title="' . __( 'Sign Up', 'woothemes-sensei' )  . '">' . __( 'course', 'woothemes-sensei' ) . '</a>' ); ?></div>
3524
3525
                <?php } ?>
3526
3527
            <?php } else { ?>
3528
3529
            <?php if( ! Sensei_Utils::user_started_course( $course_id, get_current_user_id() ) &&  sensei_is_login_required() )  : ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression \Sensei_Utils::user_star... get_current_user_id()) of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug introduced by
It seems like $course_id defined by Sensei()->lesson->get_course_id(get_the_ID()) on line 3471 can also be of type boolean; however, Sensei_Utils::user_started_course() does only seem to accept integer, 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...
3530
3531
                <div class="sensei-message info">
3532
                    <?php
3533
                    $course_link =  '<a href="'
3534
                                        . esc_url( get_permalink( $course_id ) )
3535
                                        . '" title="' . __( 'Sign Up', 'woothemes-sensei' )
3536
                                        . '">' . __( 'course', 'woothemes-sensei' )
3537
                                    . '</a>';
3538
3539
                    echo sprintf( __( 'Please sign up for the %1$s before starting the lesson.', 'woothemes-sensei' ),  $course_link );
3540
                    ?>
3541
                </div>
3542
3543
            <?php endif; ?>
3544
3545
            <?php } // End If Statement ?>
3546
3547
        </section>
3548
3549
        <?php
3550
    }// end course_signup_link
3551
3552
    /**
3553
     * Show a message telling the user to complete the previous message if they haven't done so yet
3554
     *
3555
     * @since 1.9.0
3556
     */
3557
    public  static function prerequisite_complete_message(){
3558
3559
        $lesson_prerequisite =  WooThemes_Sensei_Lesson::get_lesson_prerequisite_id( get_the_ID() );
3560
        $lesson_has_pre_requisite = $lesson_prerequisite > 0;
3561
        if ( ! WooThemes_Sensei_Lesson::is_prerequisite_complete(  get_the_ID(), get_current_user_id() ) && $lesson_has_pre_requisite ) {
3562
3563
            $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>';
3564
            echo sprintf( __( 'You must first complete %1$s before viewing this Lesson', 'woothemes-sensei' ), $prerequisite_lesson_link );
3565
3566
        }
3567
3568
    }
3569
3570
    /**
3571
     * Deprecate the sensei_lesson_archive_header hook but keep it
3572
     * active for backwards compatibility.
3573
     *
3574
     * @deprecated since 1.9.0
3575
     */
3576
    public static function deprecate_sensei_lesson_archive_header_hook(){
3577
3578
        sensei_do_deprecated_action('sensei_lesson_archive_header', '1.9.0', 'sensei_loop_lesson_inside_before');
3579
3580
    }
3581
3582
    /**
3583
     * Outputs the the lesson archive header.
3584
     *
3585
     * @since  1.9.0
3586
     * @return void
3587
     */
3588
    public function the_archive_header( ) {
3589
3590
        $before_html = '<header class="archive-header"><h1>';
3591
        $after_html = '</h1></header>';
3592
        $html = $before_html .  __( 'Lessons Archive', 'woothemes-sensei' ) . $after_html;
3593
3594
        echo apply_filters( 'sensei_lesson_archive_title', $html );
3595
3596
    } // sensei_course_archive_header()
3597
3598
    /**
3599
     * Output the title for the single lesson page
3600
     *
3601
     * @global $post
3602
     * @since 1.9.0
3603
     */
3604
    public static function the_title(){
3605
3606
        global $post;
3607
3608
        ?>
3609
        <header>
3610
3611
            <h1>
3612
3613
                <?php
3614
                /**
3615
                 * Filter documented in class-sensei-messages.php the_title
3616
                 */
3617
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
3618
                ?>
3619
3620
            </h1>
3621
3622
        </header>
3623
3624
        <?php
3625
3626
    }//the_title
3627
3628
    /**
3629
     * Flush the rewrite rules for a lesson post type
3630
     *
3631
     * @since 1.9.0
3632
     *
3633
     * @param $post_id
3634
     */
3635 View Code Duplication
    public static function flush_rewrite_rules( $post_id ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3636
3637
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
3638
3639
            return;
3640
3641
        }
3642
3643
3644
        if( 'lesson' == get_post_type( $post_id )  ){
3645
3646
            Sensei()->initiate_rewrite_rules_flush();
3647
3648
        }
3649
3650
    }
3651
3652
    /**
3653
     * Output the quiz specific buttons and messaging on the single lesson page
3654
     *
3655
     *
3656
     * @since 1.0.0 moved here from frontend class
3657
     *
3658
     * @param int $lesson_id
3659
     * @param int $user_id
3660
     */
3661
    public static function footer_quiz_call_to_action( $lesson_id = 0, $user_id = 0 ) {
3662
3663
3664
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3665
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3666
        $lesson_prerequisite       = (int) get_post_meta( $lesson_id, '_lesson_prerequisite', true );
3667
        $lesson_course_id          = (int) get_post_meta( $lesson_id, '_lesson_course', true );
0 ignored issues
show
Unused Code introduced by
$lesson_course_id 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...
3668
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3669
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3670
        $show_actions              = is_user_logged_in() ? true : false;
3671
3672
        if( intval( $lesson_prerequisite ) > 0 ) {
3673
3674
            // If the user hasn't completed the prereq then hide the current actions
3675
            $show_actions = Sensei_Utils::user_completed_lesson( $lesson_prerequisite, $user_id );
3676
3677
        }
3678
        ?>
3679
3680
        <footer>
3681
3682
            <?php
3683
            if( $show_actions && $quiz_id && Sensei()->access_settings() ) {
3684
3685
                $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3686
                if( $has_quiz_questions ) {
3687
                    ?>
3688
3689
                    <p>
3690
3691
                        <a class="button"
3692
                           href="<?php echo esc_url_raw( get_permalink( $quiz_id ) ); ?>"
3693
                           title="<?php _e( 'View the Lesson Quiz', 'woothemes-sensei'  ); ?>">
3694
3695
                            <?php  _e( 'View the Lesson Quiz', 'woothemes-sensei' ); ?>
3696
3697
                        </a>
3698
3699
                    </p>
3700
3701
                    <?php
3702
                }
3703
3704
            } // End If Statement
3705
3706
            if ( $show_actions && ! $has_user_completed_lesson ) {
3707
3708
                sensei_complete_lesson_button();
3709
3710
            } elseif( $show_actions ) {
3711
3712
                sensei_reset_lesson_button();
3713
3714
            } // End If Statement
3715
            ?>
3716
3717
        </footer>
3718
3719
        <?php
3720
    } // End sensei_lesson_quiz_meta()
3721
3722
    /**
3723
     * Show the lesson comments. This should be used in the loop.
3724
     *
3725
     * @since 1.9.0
3726
     */
3727
    public static function output_comments(){
3728
3729
        if( ! is_user_logged_in() ){
3730
            return;
3731
        }
3732
3733
        $pre_requisite_complete = Sensei()->lesson->is_prerequisite_complete( get_the_ID(), get_current_user_id() );
3734
        $course_id = Sensei()->lesson->get_course_id( get_the_ID() );
3735
        $allow_comments = Sensei()->settings->settings[ 'lesson_comments' ];
3736
        $user_taking_course = Sensei_Utils::user_started_course($course_id );
0 ignored issues
show
Bug introduced by
It seems like $course_id defined by Sensei()->lesson->get_course_id(get_the_ID()) on line 3734 can also be of type boolean; however, Sensei_Utils::user_started_course() does only seem to accept integer, 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...
3737
3738
        $lesson_allow_comments = $allow_comments && $pre_requisite_complete  && $user_taking_course;
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_taking_course of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3739
3740
        if (  $lesson_allow_comments || is_singular( 'sensei_message' ) ) {
3741
3742
            comments_template();
3743
3744
        } // End If Statement
3745
3746
    } //output_comments
3747
3748
    /**
3749
     * Display the leeson quiz status if it should be shown
3750
     *
3751
     * @param int $lesson_id defaults to the global lesson id
3752
     * @param int $user_id defaults to the current user id
3753
     *
3754
     * @since 1.9.0
3755
     */
3756
    public static function user_lesson_quiz_status_message( $lesson_id = 0, $user_id = 0){
3757
3758
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3759
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3760
        $lesson_course_id          = (int) get_post_meta( $lesson_id, '_lesson_course', true );
3761
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3762
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3763
3764
3765
        if ( $quiz_id && is_user_logged_in()
3766
            && Sensei_Utils::user_started_course( $lesson_course_id, $user_id ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \Sensei_Utils::user_star...on_course_id, $user_id) of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3767
3768
            $no_quiz_count = 0;
0 ignored issues
show
Unused Code introduced by
$no_quiz_count 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...
3769
            $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3770
3771
            // Display lesson quiz status message
3772
            if ( $has_user_completed_lesson || $has_quiz_questions ) {
3773
                $status = Sensei_Utils::sensei_user_quiz_status_message( $lesson_id, $user_id, true );
3774
                echo '<div class="sensei-message ' . $status['box_class'] . '">' . $status['message'] . '</div>';
3775
                if( $has_quiz_questions ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies 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 if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

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

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3776
                   // echo $status['extra'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3777
                } // End If Statement
3778
            } // End If Statement
3779
3780
        }
3781
3782
    }
3783
3784
} // End Class
3785
3786
/**
3787
 * Class WooThemes_Sensei_Lesson
3788
 * @ignore only for backward compatibility
3789
 * @since 1.9.0
3790
 */
3791
class WooThemes_Sensei_Lesson extends Sensei_Lesson{}
3792