Completed
Push — master ( d07982...cb7231 )
by Dan
05:09
created

Sensei_Lesson::all_lessons_edit_fields()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 93
Code Lines 52

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 93
rs 8.233
cc 6
eloc 52
nc 3
nop 2

How to fix   Long Method   

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', 'sensei-core-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
            || ! current_user_can( 'edit_lessons' ) ) {
1831
			die('');
1832
		} // End If Statement
1833
		// Parse POST data
1834
		$data = $_POST['data'];
1835
		$course_data = array();
1836
		parse_str($data, $course_data);
1837
		// Save the Course
1838
		$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...
1839
		$current_user = wp_get_current_user();
1840
		$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...
1841
		$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...
1842
		echo $updated;
1843
		die(); // WordPress may print out a spurious zero without this can be particularly bad if using JSON
1844
	} // End lesson_add_course()
1845
1846
	/**
1847
	 * lesson_update_question function.
1848
	 *
1849
	 * @access public
1850
	 * @return void
1851
	 */
1852
	public function lesson_update_question () {
1853
		global $current_user;
1854
		//Add nonce security to the request
1855
		if ( isset($_POST['lesson_update_question_nonce']) ) {
1856
			$nonce = esc_html( $_POST['lesson_update_question_nonce'] );
1857
		} // End If Statement
1858
		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...
1859
            ||  ! current_user_can( 'edit_questions' )) {
1860
1861
			die('');
1862
1863
		} // End If Statement
1864
1865
		// Parse POST data
1866
		// WP slashes all incoming data regardless of Magic Quotes setting (see wp_magic_quotes()), which means that
1867
		// even the $_POST['data'] encoded with encodeURIComponent has it's apostrophes slashed.
1868
		// So first restore the original unslashed apostrophes by removing those slashes
1869
		$data = wp_unslash( $_POST['data'] );
1870
		// Then parse the string to an array (note that parse_str automatically urldecodes all the variables)
1871
		$question_data = array();
1872
		parse_str($data, $question_data);
1873
		// Finally re-slash all elements to ensure consistancy for lesson_save_question()
1874
		$question_data = wp_slash( $question_data );
1875
		// Save the question
1876
		$return = false;
1877
		// Question Save and Delete logic
1878
		if ( isset( $question_data['action'] ) && ( $question_data['action'] == 'delete' ) ) {
1879
			// Delete the Question
1880
			$return = $this->lesson_delete_question($question_data);
1881
		} else {
1882
			// Save the Question
1883
			if ( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1884
				$current_user = wp_get_current_user();
1885
				$question_data['post_author'] = $current_user->ID;
1886
				$question_id = $this->lesson_save_question( $question_data );
1887
				$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 1886 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...
1888
1889
				$question_count = intval( $question_data['question_count'] );
1890
				++$question_count;
1891
1892
				$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 1886 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...
1893
			} // End If Statement
1894
		} // End If Statement
1895
1896
		echo $return;
1897
1898
		die();
1899
	} // End lesson_update_question()
1900
1901
	public function lesson_add_multiple_questions() {
1902
1903
		$return = '';
1904
1905
		//Add nonce security to the request
1906
		$nonce = '';
1907
		if( isset( $_POST['lesson_add_multiple_questions_nonce'] ) ) {
1908
			$nonce = esc_html( $_POST['lesson_add_multiple_questions_nonce'] );
1909
		} // End If Statement
1910
1911
		if( ! wp_verify_nonce( $nonce, 'lesson_add_multiple_questions_nonce' )
1912
            || ! current_user_can( 'edit_lessons' ) ) {
1913
			die( $return );
1914
		} // End If Statement
1915
1916
		// Parse POST data
1917
		$data = $_POST['data'];
1918
		$question_data = array();
1919
		parse_str( $data, $question_data );
1920
1921
		if( is_array( $question_data ) ) {
1922
			if( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1923
1924
				$quiz_id = intval( $question_data['quiz_id'] );
1925
				$question_number = intval( $question_data['question_number'] );
1926
				$question_category = intval( $question_data['question_category'] );
1927
1928
				$question_counter = intval( $question_data['question_count'] );
1929
				++$question_counter;
1930
1931
				$cat = get_term( $question_category, 'question-category' );
1932
1933
				$post_data = array(
1934
					'post_content' => '',
1935
					'post_status' => 'publish',
1936
					'post_title' => sprintf( __( '%1$s Question(s) from %2$s', 'woothemes-sensei' ), $question_number, $cat->name ),
1937
					'post_type' => 'multiple_question'
1938
				);
1939
1940
				$multiple_id = wp_insert_post( $post_data );
1941
1942
				if( $multiple_id && ! is_wp_error( $multiple_id ) ) {
1943
					add_post_meta( $multiple_id, 'category', $question_category );
1944
					add_post_meta( $multiple_id, 'number', $question_number );
1945
					add_post_meta( $multiple_id, '_quiz_id', $quiz_id, false );
1946
					add_post_meta( $multiple_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_counter );
1947
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
1948
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
1949
					$return = $this->quiz_panel_question( 'category', $question_counter, $multiple_id, 'quiz', array( $cat->name, $question_number ) );
1950
				}
1951
			}
1952
		}
1953
1954
		echo $return;
1955
1956
		die();
1957
	}
1958
1959
	public function lesson_remove_multiple_questions() {
1960
1961
		//Add nonce security to the request
1962
		$nonce = '';
1963
		if( isset( $_POST['lesson_remove_multiple_questions_nonce'] ) ) {
1964
			$nonce = esc_html( $_POST['lesson_remove_multiple_questions_nonce'] );
1965
		} // End If Statement
1966
1967
		if( ! wp_verify_nonce( $nonce, 'lesson_remove_multiple_questions_nonce' )
1968
        || ! current_user_can( 'edit_lessons' ) ) {
1969
			die('');
1970
		} // End If Statement
1971
1972
		// Parse POST data
1973
		$data = $_POST['data'];
1974
		$question_data = array();
1975
		parse_str( $data, $question_data );
1976
1977
		if( is_array( $question_data ) ) {
1978
			wp_delete_post( $question_data['question_id'], true );
1979
		}
1980
1981
		die( 'Deleted' );
1982
	}
1983
1984
	public function get_question_category_limit() {
1985
1986
		// Set default
1987
		$return = 1;
1988
1989
		// Parse POST data
1990
		$data = $_POST['data'];
1991
		$cat_data = array();
1992
		parse_str( $data, $cat_data );
1993
1994
		if( isset( $cat_data['cat'] ) && '' != $cat_data['cat'] ) {
1995
			$cat = get_term( $cat_data['cat'], 'question-category' );
1996
			if( isset( $cat->count ) ) {
1997
				$return = $cat->count;
1998
			}
1999
		}
2000
2001
		echo $return;
2002
2003
		die('');
2004
	}
2005
2006
	public function lesson_add_existing_questions() {
2007
2008
		//Add nonce security to the request
2009
		$nonce = '';
2010
		if( isset( $_POST['lesson_add_existing_questions_nonce'] ) ) {
2011
			$nonce = esc_html( $_POST['lesson_add_existing_questions_nonce'] );
2012
		} // End If Statement
2013
2014
		if( ! wp_verify_nonce( $nonce, 'lesson_add_existing_questions_nonce' )
2015
        || ! current_user_can( 'edit_lessons' ) ) {
2016
			die('');
2017
		} // End If Statement
2018
2019
		// Parse POST data
2020
		$data = $_POST['data'];
2021
		$question_data = array();
2022
		parse_str( $data, $question_data );
2023
2024
		$return = '';
2025
2026
		if( is_array( $question_data ) ) {
2027
2028
			if( isset( $question_data['questions'] ) && '' != $question_data['questions'] ) {
2029
2030
				$questions = explode( ',', trim( $question_data['questions'], ',' ) );
2031
				$quiz_id = $question_data['quiz_id'];
2032
				$question_count = intval( $question_data['question_count'] );
2033
2034
				foreach( $questions as $question_id ) {
2035
2036
					++$question_count;
2037
2038
					$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2039 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...
2040
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2041
						$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2042
						update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2043
			    	}
2044
2045
			    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2046
					$question_type = Sensei()->question->get_question_type( $question_id );
2047
2048
					$return .= $this->quiz_panel_question( $question_type, $question_count, $question_id );
2049
				}
2050
			}
2051
		}
2052
2053
		echo $return;
2054
2055
		die('');
2056
	}
2057
2058 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...
2059
		//Add nonce security to the request
2060
		if ( isset($_POST['lesson_update_grade_type_nonce']) ) {
2061
2062
			$nonce = esc_html( $_POST['lesson_update_grade_type_nonce'] );
2063
2064
		} // End If Statement
2065
2066
		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...
2067
        || ! current_user_can( 'edit_lessons' ) ) {
2068
2069
			die('');
2070
2071
		} // End If Statement
2072
2073
		// Parse POST data
2074
		$data = $_POST['data'];
2075
		$quiz_data = array();
2076
		parse_str($data, $quiz_data);
2077
		update_post_meta( $quiz_data['quiz_id'], '_quiz_grade_type', $quiz_data['quiz_grade_type'] );
2078
		die();
2079
	}
2080
2081
	public function lesson_update_question_order() {
2082
		// Add nonce security to the request
2083
		if ( isset($_POST['lesson_update_question_order_nonce']) ) {
2084
			$nonce = esc_html( $_POST['lesson_update_question_order_nonce'] );
2085
		} // End If Statement
2086
2087
        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...
2088
            ||! current_user_can( 'edit_lessons' ) ) {
2089
			die('');
2090
		} // End If Statement
2091
2092
		// Parse POST data
2093
		$data = $_POST['data'];
2094
		$quiz_data = array();
2095
		parse_str($data, $quiz_data);
2096
		if( strlen( $quiz_data['question_order'] ) > 0 ) {
2097
			$questions = explode( ',', $quiz_data['question_order'] );
2098
			$o = 1;
2099
			foreach( $questions as $question_id ) {
2100
				update_post_meta( $question_id, '_quiz_question_order' . $quiz_data['quiz_id'], $quiz_data['quiz_id'] . '000' . $o );
2101
				++$o;
2102
			}
2103
			update_post_meta( $quiz_data['quiz_id'], '_question_order', $questions );
2104
		}
2105
		die();
2106
	}
2107
2108 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...
2109
		//Add nonce security to the request
2110
		if ( isset($_POST['lesson_update_question_order_random_nonce']) ) {
2111
			$nonce = esc_html( $_POST['lesson_update_question_order_random_nonce'] );
2112
		} // End If Statement
2113
		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...
2114
            || ! current_user_can( 'edit_lessons' ) ) {
2115
2116
			die('');
2117
2118
		} // End If Statement
2119
		// Parse POST data
2120
		$data = $_POST['data'];
2121
		$quiz_data = array();
2122
		parse_str($data, $quiz_data);
2123
		update_post_meta( $quiz_data['quiz_id'], '_random_question_order', $quiz_data['random_question_order'] );
2124
		die();
2125
	}
2126
2127
	/**
2128
	 * lesson_save_course function.
2129
	 *
2130
	 * @access private
2131
	 * @param array $data (default: array())
2132
	 * @return integer|boolean $course_id or false
2133
	 */
2134
	private function lesson_save_course( $data = array() ) {
2135
		global $current_user;
2136
		$return = false;
2137
		// Setup the course data
2138
		$course_id = 0;
2139
		$course_content = '';
2140
		$course_title = '';
2141
		$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...
2142
		$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...
2143 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...
2144
			$course_id = absint( $data[ 'course_id' ] );
2145
		} // End If Statement
2146
		if ( isset( $data[ 'course_title' ] ) && ( '' != $data[ 'course_title' ] ) ) {
2147
			$course_title = $data[ 'course_title' ];
2148
		} // End If Statement
2149
		$post_title = $course_title;
2150
		if ( isset($data[ 'post_author' ]) ) {
2151
			$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...
2152
		} else {
2153
			$current_user = wp_get_current_user();
2154
			$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...
2155
		} // End If Statement
2156
		$post_status = 'publish';
2157
		$post_type = 'course';
2158
		if ( isset( $data[ 'course_content' ] ) && ( '' != $data[ 'course_content' ] ) ) {
2159
			$course_content = $data[ 'course_content' ];
2160
		} // End If Statement
2161
		$post_content = $course_content;
2162
		// Course Query Arguments
2163
		$post_type_args = array(	'post_content' => $post_content,
2164
  		    						'post_status' => $post_status,
2165
  		    						'post_title' => $post_title,
2166
  		    						'post_type' => $post_type
2167
  		    						);
2168
  		// Only save if there is a valid title
2169
  		if ( $post_title != '' ) {
2170
  		    // Check for prerequisite courses & product id
2171
  		    $course_prerequisite_id = absint( $data[ 'course_prerequisite' ] );
2172
  		    $course_woocommerce_product_id = absint( $data[ 'course_woocommerce_product' ] );
2173
  		    $course_category_id = absint( $data[ 'course_category' ] );
2174
  		    if ( 0 == $course_woocommerce_product_id ) { $course_woocommerce_product_id = '-'; }
2175
  		    // Insert or Update the Lesson Quiz
2176
		    if ( 0 < $course_id ) {
2177
		    	$post_type_args[ 'ID' ] = $course_id;
2178
		    	$course_id = wp_update_post($post_type_args);
2179
		    	update_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2180
		    	update_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2181
		    	if ( 0 < $course_category_id ) {
2182
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2183
		    	} // End If Statement
2184
		    } else {
2185
		    	$course_id = wp_insert_post($post_type_args);
2186
		    	add_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2187
		    	add_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2188
		    	if ( 0 < $course_category_id ) {
2189
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2190
		    	} // End If Statement
2191
		    } // End If Statement
2192
		} // End If Statement
2193
  		// Check that the insert or update saved by testing the post id
2194
  		if ( 0 < $course_id ) {
2195
  			$return = $course_id;
2196
  		} // End If Statement
2197
  		return $return;
2198
  	} // End lesson_save_course()
2199
2200
2201
	/**
2202
	 * lesson_save_question function.
2203
	 *
2204
	 * @access private
2205
	 * @param array $data (default: array())
2206
	 * @return integer|boolean $question_id or false
2207
	 */
2208
	public function lesson_save_question( $data = array(), $context = 'quiz' ) {
2209
		$return = false;
2210
		// Save the Questions
2211
		// Setup the Question data
2212
		$question_id = 0;
2213
		$question_text = '';
2214
		$question_right_answer = '';
2215
		$question_wrong_answers = $question_right_answers = array();
2216
		$question_type = 'multiple-choice';
2217
		$question_category = '';
2218
2219
		// Handle Question Type
2220
		if ( isset( $data[ 'question_type' ] ) && ( '' != $data[ 'question_type' ] ) ) {
2221
			$question_type = $data[ 'question_type' ];
2222
		} // End If Statement
2223
2224
		if ( isset( $data[ 'question_category' ] ) && ( '' != $data[ 'question_category' ] ) ) {
2225
			$question_category = $data[ 'question_category' ];
2226
		} // End If Statement
2227
2228 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...
2229
			$question_id = absint( $data[ 'question_id' ] );
2230
		} // End If Statement
2231
		if ( isset( $data[ 'question' ] ) && ( '' != $data[ 'question' ] ) ) {
2232
			$question_text = $data[ 'question' ];
2233
		} // End If Statement
2234
		$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...
2235
		// Handle Default Fields (multiple choice)
2236
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answers' ] ) && ( '' != $data[ 'question_right_answers' ] ) ) {
2237
			$question_right_answers = $data[ 'question_right_answers' ];
2238
		} // End If Statement
2239
		elseif ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answer' ] ) && ( '' != $data[ 'question_right_answer' ] ) ) {
2240
			$question_right_answer = $data[ 'question_right_answer' ];
2241
		} // End If Statement
2242
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_wrong_answers' ] ) && ( '' != $data[ 'question_wrong_answers' ] ) ) {
2243
			$question_wrong_answers = $data[ 'question_wrong_answers' ];
2244
		} // End If Statement
2245
		// Handle Boolean Fields - Edit
2246
		if ( 'boolean' == $question_type && isset( $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) && ( '' != $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) ) {
2247
			$question_right_answer = $data[ 'question_' . $question_id . '_right_answer_boolean' ];
2248
		} // End If Statement
2249
		// Handle Boolean Fields - Add
2250
		if ( 'boolean' == $question_type && isset( $data[ 'question_right_answer_boolean' ] ) && ( '' != $data[ 'question_right_answer_boolean' ] ) ) {
2251
			$question_right_answer = $data[ 'question_right_answer_boolean' ];
2252
		} // End If Statement
2253
		// Handle Gap Fill Fields
2254
		if ( 'gap-fill' == $question_type && isset( $data[ 'add_question_right_answer_gapfill_gap' ] ) && '' != $data[ 'add_question_right_answer_gapfill_gap' ] ) {
2255
			$question_right_answer = $data[ 'add_question_right_answer_gapfill_pre' ] . '||' . $data[ 'add_question_right_answer_gapfill_gap' ] . '||' . $data[ 'add_question_right_answer_gapfill_post' ];
2256
		} // End If Statement
2257
		// Handle Multi Line Fields
2258
		if ( 'multi-line' == $question_type && isset( $data[ 'add_question_right_answer_multiline' ] ) && ( '' != $data[ 'add_question_right_answer_multiline' ] ) ) {
2259
			$question_right_answer = $data[ 'add_question_right_answer_multiline' ];
2260
		} // End If Statement
2261
		// Handle Single Line Fields
2262
		if ( 'single-line' == $question_type && isset( $data[ 'add_question_right_answer_singleline' ] ) && ( '' != $data[ 'add_question_right_answer_singleline' ] ) ) {
2263
			$question_right_answer = $data[ 'add_question_right_answer_singleline' ];
2264
		} // End If Statement
2265
		// Handle File Upload Fields
2266
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_right_answer_fileupload' ] ) && ( '' != $data[ 'add_question_right_answer_fileupload' ] ) ) {
2267
			$question_right_answer = $data[ 'add_question_right_answer_fileupload' ];
2268
		} // End If Statement
2269
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_wrong_answer_fileupload' ] ) && ( '' != $data[ 'add_question_wrong_answer_fileupload' ] ) ) {
2270
			$question_wrong_answers = array( $data[ 'add_question_wrong_answer_fileupload' ] );
2271
		} // End If Statement
2272
2273
		// Handle Question Grade
2274
		if ( isset( $data[ 'question_grade' ] ) && ( '' != $data[ 'question_grade' ] ) ) {
2275
			$question_grade = $data[ 'question_grade' ];
2276
		} // End If Statement
2277
2278
		// Handle Answer Feedback
2279
		$answer_feedback = '';
2280
		if ( isset( $data[ 'answer_feedback_boolean' ] ) && !empty( $data[ 'answer_feedback_boolean' ] ) ) {
2281
2282
            $answer_feedback = $data[ 'answer_feedback_boolean' ];
2283
2284
		}elseif( isset( $data[ 'answer_feedback_multiple_choice' ] ) && !empty( $data[ 'answer_feedback_multiple_choice' ] ) ){
2285
2286
            $answer_feedback = $data[ 'answer_feedback_multiple_choice' ];
2287
2288
        }elseif( isset( $data[ 'answer_feedback' ] )  ){
2289
2290
            $answer_feedback = $data[ 'answer_feedback' ];
2291
2292
        } // End If Statement
2293
2294
		$post_title = $question_text;
2295
		$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...
2296
		$post_status = 'publish';
2297
		$post_type = 'question';
2298
		// Handle the extended question text
2299
		if ( isset( $data[ 'question_description' ] ) && ( '' != $data[ 'question_description' ] ) ) {
2300
			$post_content = $data[ 'question_description' ];
2301
		}
2302
		else {
2303
			$post_content = '';
2304
		}
2305
		// Question Query Arguments
2306
		$post_type_args = array(	'post_content' => $post_content,
2307
  		    						'post_status' => $post_status,
2308
  		    						'post_title' => $post_title,
2309
  		    						'post_type' => $post_type
2310
  		    						);
2311
2312
  		// Remove empty values and reindex the array
2313
  		if ( is_array( $question_right_answers ) && 0 < count($question_right_answers) ) {
2314
  			$question_right_answers_array = array_values( array_filter( $question_right_answers, 'strlen' ) );
2315
  			$question_right_answers = array();
2316
2317
  			foreach( $question_right_answers_array as $answer ) {
2318
  				if( ! in_array( $answer, $question_right_answers ) ) {
2319
  					$question_right_answers[] = $answer;
2320
  				}
2321
  			}
2322
  			if ( 0 < count($question_right_answers) ) {
2323
  				$question_right_answer = $question_right_answers;
2324
  			}
2325
  		} // End If Statement
2326
  		$right_answer_count = count( $question_right_answer );
2327
2328
		// Remove empty values and reindex the array
2329
  		if ( is_array( $question_wrong_answers ) ) {
2330
  			$question_wrong_answers_array = array_values( array_filter( $question_wrong_answers, 'strlen' ) );
2331
  			$question_wrong_answers = array();
2332
  		} // End If Statement
2333
2334
  		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...
2335
  			if( ! in_array( $answer, $question_wrong_answers ) ) {
2336
  				$question_wrong_answers[] = $answer;
2337
  			}
2338
  		}
2339
2340
  		$wrong_answer_count = count( $question_wrong_answers );
2341
2342
  		// Only save if there is a valid title
2343
  		if ( $post_title != '' ) {
2344
2345
  			// Get Quiz ID for the question
2346
  		    $quiz_id = $data['quiz_id'];
2347
2348
  		    // Get question media
2349
			$question_media = $data['question_media'];
2350
2351
  		    // Get answer order
2352
  		    $answer_order = '';
2353
  		    if( isset( $data['answer_order'] ) ) {
2354
				$answer_order = $data['answer_order'];
2355
			}
2356
2357
			// Get random order selection
2358
			$random_order = 'no';
2359
			if( isset( $data['random_order'] ) ) {
2360
				$random_order = $data['random_order'];
2361
			}
2362
2363
  		    // Insert or Update the question
2364
  		    if ( 0 < $question_id ) {
2365
2366
  		    	$post_type_args[ 'ID' ] = $question_id;
2367
		    	$question_id = wp_update_post( $post_type_args );
2368
2369
		    	// Update poast meta
2370
		    	if( 'quiz' == $context ) {
2371
		    		$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2372
		    		if( ! in_array( $quiz_id, $quizzes ) ) {
2373
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2374
			    	}
2375
		    	}
2376
2377
		    	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...
2378
		    	update_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2379
		    	update_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2380
		    	update_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2381
		    	update_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2382
		    	update_post_meta( $question_id, '_question_media', $question_media );
2383
		    	update_post_meta( $question_id, '_answer_order', $answer_order );
2384
		    	update_post_meta( $question_id, '_random_order', $random_order );
2385
2386
		    	if( 'quiz' != $context ) {
2387
		    		wp_set_post_terms( $question_id, array( $question_type ), 'question-type', false );
2388
		    	}
2389
				// Don't store empty value, no point
2390
				if ( !empty($answer_feedback) ) {
2391
					update_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2392
				}
2393
2394
		    } else {
2395
				$question_id = wp_insert_post( $post_type_args );
2396
				$question_count = intval( $data['question_count'] );
2397
				++$question_count;
2398
2399
				// Set post meta
2400 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...
2401
					add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2402
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2403
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2404
				}
2405
2406
				if( isset( $question_grade ) ) {
2407
		    		add_post_meta( $question_id, '_question_grade', $question_grade );
2408
		    	}
2409
		    	add_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2410
		    	add_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2411
		    	add_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2412
		    	add_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2413
		    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2414
		    	add_post_meta( $question_id, '_question_media', $question_media );
2415
		    	add_post_meta( $question_id, '_answer_order', $answer_order );
2416
		    	add_post_meta( $question_id, '_random_order', $random_order );
2417
				// Don't store empty value, no point
2418
				if ( !empty($answer_feedback) ) {
2419
					add_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2420
				}
2421
2422
		    	// Set the post terms for question-type
2423
			    wp_set_post_terms( $question_id, array( $question_type ), 'question-type' );
2424
2425
			    if( $question_category ) {
2426
	    			wp_set_post_terms( $question_id, array( $question_category ), 'question-category' );
2427
	    		}
2428
2429
		    } // End If Statement
2430
		} // End If Statement
2431
  		// Check that the insert or update saved by testing the post id
2432
  		if ( 0 < $question_id ) {
2433
  			$return = $question_id;
2434
  		} // End If Statement
2435
  		return $return;
2436
  	} // End lesson_question_save()
2437
2438
2439
	/**
2440
	 * lesson_delete_question function.
2441
	 *
2442
	 * @access private
2443
	 * @param array $data (default: array())
2444
	 * @return boolean
2445
	 */
2446
	private function lesson_delete_question( $data = array() ) {
2447
2448
		// Get which question to delete
2449
		$question_id = 0;
2450 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...
2451
			$question_id = absint( $data[ 'question_id' ] );
2452
		} // End If Statement
2453
		// Delete the question
2454
		if ( 0 < $question_id ) {
2455
			$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2456
2457
			foreach( $quizzes as $quiz_id ) {
2458
				if( $quiz_id == $data['quiz_id'] ) {
2459
					delete_post_meta( $question_id, '_quiz_id', $quiz_id );
2460
				}
2461
			}
2462
2463
			return true;
2464
		} // End If Statement
2465
		return false;
2466
	} // End lesson_delete_question()
2467
2468
2469
	/**
2470
	 * lesson_complexities function.
2471
	 *
2472
	 * @access public
2473
	 * @return array $lesson_complexities
2474
	 */
2475
	public function lesson_complexities() {
2476
2477
		// V2 - make filter for this array
2478
        $lesson_complexities = array( 	'easy' => __( 'Easy', 'woothemes-sensei' ),
2479
									'std' => __( 'Standard', 'woothemes-sensei' ),
2480
									'hard' => __( 'Hard', 'woothemes-sensei' )
2481
									);
2482
2483
		return $lesson_complexities;
2484
2485
	} // End lesson_complexities
2486
2487
2488
	/**
2489
	 * lesson_count function.
2490
	 *
2491
	 * @access public
2492
	 * @param string $post_status (default: 'publish')
2493
	 * @return int
2494
	 */
2495
	public function lesson_count( $post_status = 'publish', $course_id = false ) {
2496
2497
		$post_args = array(	'post_type'         => 'lesson',
2498
							'posts_per_page'    => -1,
2499
//							'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...
2500
//							'order'             => 'ASC',
2501
							'post_status'       => $post_status,
2502
							'suppress_filters'  => 0,
2503
							'fields'            => 'ids',
2504
							);
2505
		if( $course_id ) {
2506
			$post_args['meta_query'][] = array(
2507
				'key' => '_lesson_course',
2508
				'value' => $course_id,
2509
			);
2510
		}
2511
		else {
2512
			// Simple check for connection to a Course
2513
			$post_args['meta_query'][] = array(
2514
				'key' => '_lesson_course',
2515
				'value' => 0,
2516
				'compare' => '>=',
2517
			);
2518
		}
2519
2520
		// Allow WP to generate the complex final query, just shortcut to only do an overall count
2521
//		add_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
2522
		$lessons_query = new WP_Query( apply_filters( 'sensei_lesson_count', $post_args ) );
2523
//		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...
2524
2525
		return count( $lessons_query->posts );
2526
	} // End lesson_count()
2527
2528
2529
	/**
2530
	 * lesson_quizzes function.
2531
	 *
2532
	 * @access public
2533
	 * @param int $lesson_id (default: 0)
2534
	 * @param string $post_status (default: 'publish')
2535
	 * @param string $fields (default: 'ids')
2536
	 * @return int $quiz_id
2537
	 */
2538 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...
2539
2540
		$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...
2541
2542
		$post_args = array(	'post_type' 		=> 'quiz',
2543
							'posts_per_page' 		=> 1,
2544
							'orderby'         	=> 'title',
2545
    						'order'           	=> 'DESC',
2546
    						'post_parent'      	=> $lesson_id,
2547
    						'post_status'		=> $post_status,
2548
							'suppress_filters' 	=> 0,
2549
							'fields'            => $fields
2550
							);
2551
		$posts_array = get_posts( $post_args );
2552
        $quiz_id = array_shift($posts_array);
2553
2554
		return $quiz_id;
2555
	} // End lesson_quizzes()
2556
2557
2558
	/**
2559
	 * Fetches all the questions for a quiz depending on certain conditions.
2560
     *
2561
     * Determine which questions should be shown depending on:
2562
     * - admin/teacher selected questions to be shown
2563
     * - questions shown to a user previously (saved as asked questions)
2564
     * - limit number of questions lesson setting
2565
	 *
2566
     * @since 1.0
2567
	 * @param int $quiz_id (default: 0)
2568
	 * @param string $post_status (default: 'publish')
2569
	 * @param string $orderby (default: 'meta_value_num title')
2570
	 * @param string $order (default: 'ASC')
2571
     *
2572
	 * @return array $questions { $question type WP_Post }
2573
	 */
2574
	public function lesson_quiz_questions( $quiz_id = 0, $post_status = 'any', $orderby = 'meta_value_num title', $order = 'ASC' ) {
2575
2576
		$quiz_id = (string) $quiz_id;
2577
        $quiz_lesson_id = Sensei()->quiz->get_lesson_id( $quiz_id );
2578
2579
        // setup the user id
2580
        if( is_admin() ) {
2581
            $user_id = isset( $_GET['user'] ) ? $_GET['user'] : '' ;
2582
        } else {
2583
            $user_id = get_current_user_id();
2584
        }
2585
2586
        // get the users current status on the lesson
2587
        $user_lesson_status = Sensei_Utils::user_lesson_status( $quiz_lesson_id, $user_id );
2588
2589
		// Set the default question order if it has not already been set for this quiz
2590
		$this->set_default_question_order( $quiz_id );
2591
2592
		// If viewing quiz on the frontend then show questions in random order if set
2593
		if ( ! is_admin() ) {
2594
			$random_order = get_post_meta( $quiz_id, '_random_question_order', true );
2595
			if( $random_order && $random_order == 'yes' ) {
2596
				$orderby = 'rand';
2597
			}
2598
		}
2599
2600
		// Get all questions and multiple questions
2601
		$question_query_args = array(
2602
			'post_type' 		=> array( 'question', 'multiple_question' ),
2603
			'posts_per_page' 	=> -1,
2604
			'meta_key'        	=> '_quiz_question_order' . $quiz_id,
2605
			'orderby'         	=> $orderby,
2606
			'order'           	=> $order,
2607
			'meta_query'		=> array(
2608
				array(
2609
					'key'       => '_quiz_id',
2610
					'value'     => $quiz_id,
2611
				)
2612
			),
2613
			'post_status'		=> $post_status,
2614
			'suppress_filters' 	=> 0
2615
		);
2616
2617
        //query the questions
2618
		$questions_query = new WP_Query( $question_query_args );
2619
2620
        // Set return array to initially include all items
2621
        $questions = $questions_query->posts;
2622
2623
        // set the questions array that will be manipulated within this function
2624
        $questions_array = $questions_query->posts;
2625
2626
		// If viewing quiz on frontend or in grading then only single questions must be shown
2627
		$selected_questions = false;
2628
		if( ! is_admin() || ( is_admin() && isset( $_GET['page'] ) && 'sensei_grading' == $_GET['page'] && isset( $_GET['user'] ) && isset( $_GET['quiz_id'] ) ) ) {
2629
2630
			// Fetch the questions that the user was asked in their quiz if they have already completed it
2631
			$questions_asked_string = !empty( $user_lesson_status->comment_ID) ? get_comment_meta( $user_lesson_status->comment_ID, 'questions_asked', true ) : false;
2632
			if( !empty($questions_asked_string) ) {
2633
2634
				$selected_questions = explode( ',', $questions_asked_string );
2635
2636
				// Fetch each question in the order in which they were asked
2637
				$questions = array();
2638
				foreach( $selected_questions as $question_id ) {
2639
					if( ! $question_id ) continue;
2640
					$question = get_post( $question_id );
2641
					if( ! isset( $question ) || ! isset( $question->ID ) ) continue;
2642
					$questions[] = $question;
2643
				}
2644
2645
			} else {
2646
2647
				// Otherwise, make sure that we convert all multiple questions into single questions
2648
2649
				$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...
2650
				$existing_questions = array();
2651
2652
				// Set array of questions that already exist so we can prevent duplicates from appearing
2653
				foreach( $questions_array as $question ) {
2654
					if( 'question' != $question->post_type ) continue;
2655
					$existing_questions[] = $question->ID;
2656
				}
2657
2658
				// Include only single questions in the return array
2659
				$questions_loop = $questions_array;
2660
				$questions_array = array();
2661
				foreach( $questions_loop as $k => $question ) {
2662
2663
					// If this is a single question then include it
2664
					if( 'question' == $question->post_type ) {
2665
						$questions_array[] = $question;
2666
					} else {
2667
2668
						// If this is a multiple question then get the specified amount of questions from the specified category
2669
						$question_cat = intval( get_post_meta( $question->ID, 'category', true ) );
2670
						$question_number = intval( get_post_meta( $question->ID, 'number', true ) );
2671
2672
						$qargs = array(
2673
							'post_type' 		=> 'question',
2674
							'posts_per_page' 		=> $question_number,
2675
							'orderby'         	=> $orderby,
2676
							'tax_query'			=> array(
2677
								array(
2678
									'taxonomy'  => 'question-category',
2679
									'field'     => 'term_id',
2680
									'terms'		=> $question_cat
2681
								)
2682
							),
2683
							'post_status'		=> $post_status,
2684
							'suppress_filters' 	=> 0,
2685
							'post__not_in'		=> $existing_questions,
2686
						);
2687
						$cat_questions = get_posts( $qargs );
2688
2689
						// Merge results into return array
2690
						$questions_array = array_merge( $questions_array, $cat_questions );
2691
2692
						// Add selected questions to existing questions array to prevent duplicates from being added
2693
						foreach( $questions_array as $cat_question ) {
2694
							if( in_array( $cat_question->ID, $existing_questions ) ) continue;
2695
							$existing_questions[] = $cat_question->ID;
2696
						}
2697
					}
2698
				}
2699
2700
				// Set return data
2701
				$questions = $questions_array;
2702
			}
2703
		}
2704
2705
		// 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
2706
		if( ! $selected_questions ) {
2707
2708
			// Only limit questions like this on the frontend
2709
			if( ! is_admin() ) {
2710
2711
				// Get number of questions to show
2712
				$show_questions = intval( get_post_meta( $quiz_id, '_show_questions', true ) );
2713
				if( $show_questions ) {
2714
2715
					// Get random set of array keys from selected questions array
2716
					$selected_questions = array_rand( $questions_array, $show_questions );
2717
2718
					// Loop through all questions and pick the the ones to be shown based on the random key selection
2719
					$questions = array();
2720
					foreach( $questions_array as $k => $question ) {
2721
2722
						// Random keys will always be an array, unless only one question is to be shown
2723
						if( is_array( $selected_questions ) ) {
2724
							if( in_array( $k, $selected_questions ) ) {
2725
								$questions[] = $question;
2726
							}
2727
						} elseif( 1 == $show_questions ) {
2728
							if ( $selected_questions == $k ) {
2729
								$questions[] = $question;
2730
							}
2731
						}
2732
					}
2733
				}
2734
			}
2735
		}
2736
2737
        // Save the questions that will be asked for the current user
2738
        // this happens only once per user/quiz, unless the user resets the quiz
2739
        if( ! is_admin() ){
2740
2741
            if( $user_lesson_status ) {
2742
2743
                $questions_asked = get_comment_meta($user_lesson_status->comment_ID, 'questions_asked', true);
2744
                if ( empty($questions_asked) && $user_lesson_status) {
2745
2746
                    $questions_asked = array();
2747
                    foreach ($questions as $question) {
2748
2749
                        $questions_asked[] = $question->ID;
2750
2751
                    }
2752
2753
                    // save the questions asked id
2754
                    $questions_asked_csv = implode(',', $questions_asked);
2755
                    update_comment_meta($user_lesson_status->comment_ID, 'questions_asked', $questions_asked_csv);
2756
2757
                }
2758
            }
2759
        }
2760
2761
        /**
2762
         * Filter the questions returned by Sensei_Lesson::lessons_quiz_questions
2763
         *
2764
         * @hooked Sensei_Teacher::allow_teacher_access_to_questions
2765
         * @since 1.8.0
2766
         */
2767
		return apply_filters( 'sensei_lesson_quiz_questions', $questions,  $quiz_id  );
2768
2769
	} // End lesson_quiz_questions()
2770
2771
	/**
2772
	 * Set the default quiz order
2773
	 * @param integer $quiz_id ID of quiz
2774
	 */
2775
	public function set_default_question_order( $quiz_id = 0 ) {
2776
2777
		if( $quiz_id ) {
2778
2779
			$question_order = get_post_meta( $quiz_id, '_question_order', true );
2780
2781
			if( ! $question_order ) {
2782
2783
				$args = array(
2784
					'post_type' 		=> 'question',
2785
					'posts_per_page' 		=> -1,
2786
					'orderby'         	=> 'ID',
2787
					'order'           	=> 'ASC',
2788
					'meta_query'		=> array(
2789
						array(
2790
							'key'       => '_quiz_id',
2791
							'value'     => $quiz_id
2792
						)
2793
					),
2794
					'post_status'		=> 'any',
2795
					'suppress_filters' 	=> 0
2796
				);
2797
				$questions = get_posts( $args );
2798
2799
				$o = 1;
2800
				foreach( $questions as $question ) {
2801
					add_post_meta( $question->ID, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $o, true );
2802
					$o++;
2803
				}
2804
			}
2805
		}
2806
2807
	}
2808
2809
	/**
2810
	 * lesson_image function.
2811
	 *
2812
	 * Handles output of the lesson image
2813
	 *
2814
	 * @access public
2815
	 * @param int $lesson_id (default: 0)
2816
	 * @param string $width (default: '100')
2817
	 * @param string $height (default: '100')
2818
	 * @return string
2819
	 */
2820
	public function lesson_image( $lesson_id = 0, $width = '100', $height = '100', $widget = false ) {
2821
2822
		$html = '';
2823
2824
		// Get Width and Height settings
2825
		if ( ( $width == '100' ) && ( $height == '100' ) ) {
2826
2827
			if ( is_singular( 'lesson' ) ) {
2828
2829
				if ( ! $widget && ! Sensei()->settings->settings[ 'lesson_single_image_enable' ] ) {
2830
2831
					return '';
2832
2833
				} // End If Statement
2834
2835
				$image_thumb_size = 'lesson_single_image';
2836
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2837
				$width = $dimensions['width'];
2838
				$height = $dimensions['height'];
2839
				$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...
2840
2841
			} else {
2842
2843
				if ( ! $widget && ! Sensei()->settings->settings[ 'course_lesson_image_enable' ] ) {
2844
2845
					return '';
2846
				} // End If Statement
2847
2848
				$image_thumb_size = 'lesson_archive_image';
2849
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2850
				$width = $dimensions['width'];
2851
				$height = $dimensions['height'];
2852
				$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...
2853
2854
			} // End If Statement
2855
2856
		} // End If Statement
2857
2858
		$img_url = '';
2859
2860
		if ( has_post_thumbnail( $lesson_id ) ) {
2861
2862
   			// Get Featured Image
2863
   			$img_url = get_the_post_thumbnail( $lesson_id, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
2864
2865 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...
2866
2867
 			// Display Image Placeholder if none
2868
			if ( Sensei()->settings->settings[ 'placeholder_images_enable' ] ) {
2869
2870
                $img_url = apply_filters( 'sensei_lesson_placeholder_image_url', '<img src="http://placehold.it/' . $width . 'x' . $height . '" class="woo-image thumbnail alignleft" />' );
2871
2872
			} // End If Statement
2873
2874
		} // End If Statement
2875
2876
		$html .= '<a href="' . get_permalink( $lesson_id ) . '" title="' . esc_attr( get_post_field( 'post_title', $lesson_id ) ) . '">' . $img_url . '</a>';
2877
2878
		return $html;
2879
2880
	} // End lesson_image()
2881
2882
    /**
2883
     * Ooutpu the lesson image
2884
     *
2885
     * @since 1.9.0
2886
     * @param integer $lesson_id
2887
     */
2888
    public static function the_lesson_image( $lesson_id = 0 ){
2889
2890
        echo Sensei()->lesson->lesson_image( $lesson_id );
2891
2892
    }
2893
2894
	/**
2895
	 * Returns the the lesson excerpt.
2896
	 *
2897
	 * @param WP_Post $lesson
2898
     * @param bool $add_p_tags should the excerpt be wrapped by calling wpautop()
2899
	 * @return string
2900
	 */
2901
	public static function lesson_excerpt( $lesson = null, $add_p_tags = true ) {
2902
		$html = '';
2903
		if ( is_a( $lesson, 'WP_Post' ) && 'lesson' == $lesson->post_type ) {
2904
2905
            $excerpt =  $lesson->post_excerpt;
2906
2907
            // if $add_p_tags true wrap with <p> else return the excerpt as is
2908
            $html =  $add_p_tags ? wpautop( $excerpt ) : $excerpt;
2909
2910
		}
2911
		return apply_filters( 'sensei_lesson_excerpt', $html );
2912
2913
	} // End lesson_excerpt()
2914
2915
    /**
2916
     * Returns the course for a given lesson
2917
     *
2918
     * @since 1.7.4
2919
     * @access public
2920
     *
2921
     * @param int $lesson_id
2922
     * @return int|bool $course_id or bool when nothing is found.
2923
     */
2924
     public function get_course_id( $lesson_id ){
2925
2926
         if( ! isset( $lesson_id ) || empty( $lesson_id )
2927
         ||  'lesson' != get_post_type( $lesson_id ) ){
2928
             return false;
2929
         }
2930
2931
         $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true);
2932
2933
         // make sure the course id is valid
2934
         if( empty( $lesson_course_id )
2935
             || is_array( $lesson_course_id )
2936
             || intval( $lesson_course_id ) < 1
2937
             || 'course' != get_post_type( $lesson_course_id ) ){
2938
2939
             return false;
2940
2941
         }
2942
2943
         return $lesson_course_id;
2944
2945
     }// en get_course_id
2946
2947
    /**
2948
     * Add the admin all lessons screen edit options.
2949
     *
2950
     * The fields in this function work for both quick and bulk edit. The ID attributes is used
2951
     * by bulk edit javascript in the front end to retrieve the new values set byt the user. Then
2952
     * name attribute is will be used by the quick edit and submitted via standard POST. This
2953
     * will use this classes save_post_meta function to save the new field data.
2954
     *
2955
     * @hooked quick_edit_custom_box
2956
     * @hooked bulk_edit_custom_box
2957
     *
2958
     * @since 1.8.0
2959
     *
2960
     * @param string $column_name
2961
     * @param string $post_type
2962
     * @return void
2963
     */
2964
    public function all_lessons_edit_fields( $column_name, $post_type ) {
2965
2966
        // only show these options ont he lesson post type edit screen
2967
        if( 'lesson' != $post_type || 'lesson-course' != $column_name
2968
            || ! current_user_can( 'edit_lessons' ) ) {
2969
            return;
2970
        }
2971
2972
        ?>
2973
        <fieldset class="sensei-edit-field-set inline-edit-lesson">
2974
            <div class="sensei-inline-edit-col column-<?php echo $column_name ?>">
2975
                    <?php
2976
                    echo '<h4>' . __('Lesson Information', 'woothemes-sensei') . '</h4>';
2977
                    // create a nonce field to be  used as a security measure when saving the data
2978
                    wp_nonce_field( 'bulk-edit-lessons', '_edit_lessons_nonce' );
2979
                    wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce'  );
2980
2981
                    // unchanged option - we need this in because
2982
                    // the default option in bulk edit should not be empty. If it is
2983
                    // the user will erase data they didn't want to touch.
2984
                    $no_change_text = '-- ' . __('No Change', 'woothemes-sensei') . ' --';
2985
2986
                    //
2987
                    //course selection
2988
                    //
2989
                    $courses =  WooThemes_Sensei_Course::get_all_courses();
2990
                    $course_options = array();
2991
                    if ( count( $courses ) > 0 ) {
2992
                        foreach ($courses as $course ){
2993
                            $course_options[ $course->ID ] = get_the_title( $course->ID );
2994
                        }
2995
                    }
2996
                    //pre-append the no change option
2997
                    $course_options['-1']=  $no_change_text;
2998
                    $course_attributes = array( 'name'=> 'lesson_course', 'id'=>'sensei-edit-lesson-course' , 'class'=>' ' );
2999
                    $course_field =  Sensei_Utils::generate_drop_down( '-1', $course_options, $course_attributes );
3000
                    echo $this->generate_all_lessons_edit_field( __('Lesson Course', 'woothemes-sensei'),   $course_field  );
3001
3002
                    //
3003
                    // lesson complexity selection
3004
                    //
3005
                    $lesson_complexities =  $this->lesson_complexities();
3006
                    //pre-append the no change option
3007
                    $lesson_complexities['-1']=  $no_change_text;
3008
                    $complexity_dropdown_attributes = array( 'name'=> 'lesson_complexity', 'id'=>'sensei-edit-lesson-complexity' , 'class'=>' ');
3009
                    $complexity_filed =  Sensei_Utils::generate_drop_down( '-1', $lesson_complexities, $complexity_dropdown_attributes );
3010
                    echo $this->generate_all_lessons_edit_field( __('Lesson Complexity', 'woothemes-sensei'),   $complexity_filed  );
3011
3012
                    ?>
3013
3014
                    <h4><?php _e('Quiz Settings', 'woothemes-sensei'); ?> </h4>
3015
3016
                    <?php
3017
3018
                    //
3019
                    // Lesson require pass to complete
3020
                    //
3021
                    $pass_required_options = array(
3022
                        '-1' => $no_change_text,
3023
                         '0' => __('No','woothemes'),
3024
                         '1' => __('Yes','woothemes'),
3025
                    );
3026
3027
                    $pass_required_select_attributes = array( 'name'=> 'pass_required',
3028
                                                                'id'=> 'sensei-edit-lesson-pass-required',
3029
                                                                'class'=>' '   );
3030
                    $require_pass_field =  Sensei_Utils::generate_drop_down( '-1', $pass_required_options, $pass_required_select_attributes, false );
3031
                    echo $this->generate_all_lessons_edit_field( __('Pass required', 'woothemes-sensei'),   $require_pass_field  );
3032
3033
                    //
3034
                    // Quiz pass percentage
3035
                    //
3036
                    $quiz_pass_percentage_field = '<input name="quiz_passmark" id="sensei-edit-quiz-pass-percentage" type="number" />';
3037
                    echo $this->generate_all_lessons_edit_field( __('Pass Percentage', 'woothemes-sensei'), $quiz_pass_percentage_field  );
3038
3039
                    //
3040
                    // Enable quiz reset button
3041
                    //
3042
                    $quiz_reset_select__options = array(
3043
                        '-1' => $no_change_text,
3044
                        '0' => __('No','woothemes'),
3045
                        '1' => __('Yes','woothemes'),
3046
                    );
3047
                    $quiz_reset_name_id = 'sensei-edit-enable-quiz-reset';
3048
                    $quiz_reset_select_attributes = array( 'name'=> 'enable_quiz_reset', 'id'=>$quiz_reset_name_id, 'class'=>' ' );
3049
                    $quiz_reset_field =  Sensei_Utils::generate_drop_down( '-1', $quiz_reset_select__options, $quiz_reset_select_attributes, false );
3050
                    echo $this->generate_all_lessons_edit_field( __('Enable quiz reset button', 'woothemes-sensei'), $quiz_reset_field  );
3051
3052
                    ?>
3053
            </div>
3054
        </fieldset>
3055
    <?php
3056
    }// all_lessons_edit_fields
3057
3058
    /**
3059
     * Create the html for the edit field
3060
     *
3061
     * Wraps the passed in field and title combination with the correct html.
3062
     *
3063
     * @since 1.8.0
3064
     *
3065
     * @param string $title that will stand to the left of the field.
3066
     * @param string $field type markup for the field that must be wrapped.
3067
     * @return string $field_html
3068
     */
3069
    public function generate_all_lessons_edit_field( $title  ,$field ){
3070
3071
        $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...
3072
        $html = '<div class="inline-edit-group" >';
3073
        $html .=  '<span class="title">'. $title .'</span> ';
3074
        $html .= '<span class="input-text-wrap">';
3075
        $html .= $field;
3076
        $html .= '</span>';
3077
        $html .= '</label></div>';
3078
3079
        return $html ;
3080
3081
    }//end generate_all_lessons_edit_field
3082
3083
    /**
3084
     * Respond to the ajax call from the bulk edit save function. This comes
3085
     * from the admin all lesson screen.
3086
     *
3087
     * @since 1.8.0
3088
     * @return void
3089
     */
3090
    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...
3091
3092
        // verify all the data before attempting to save
3093
        if( ! isset( $_POST['security'] ) || ! check_ajax_referer( 'bulk-edit-lessons', 'security' )
3094
            ||  empty( $_POST[ 'post_ids' ] )  || ! is_array( $_POST[ 'post_ids' ] ) ) {
3095
            die();
3096
        }
3097
3098
        // get our variables
3099
        $new_course = sanitize_text_field(  $_POST['sensei_edit_lesson_course'] );
3100
        $new_complexity = sanitize_text_field(  $_POST['sensei_edit_complexity'] );
3101
        $new_pass_required = sanitize_text_field(  $_POST['sensei_edit_pass_required'] );
3102
        $new_pass_percentage = sanitize_text_field(  $_POST['sensei_edit_pass_percentage'] );
3103
        $new_enable_quiz_reset = sanitize_text_field(  $_POST['sensei_edit_enable_quiz_reset'] );
3104
        // store the values for all selected posts
3105
        foreach( $_POST[ 'post_ids' ] as $lesson_id ) {
3106
3107
            // get the quiz id needed for the quiz meta
3108
            $quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
3109
3110
            // do not save the items if the value is -1 as this
3111
            // means it was not changed
3112
3113
            // update lesson course
3114
            if( -1 != $new_course ){
3115
                update_post_meta( $lesson_id, '_lesson_course', $new_course );
3116
            }
3117
            // update lesson complexity
3118
            if( -1 != $new_complexity ){
3119
                update_post_meta( $lesson_id, '_lesson_complexity', $new_complexity );
3120
            }
3121
3122
            // Quiz Related settings
3123
            if( isset( $quiz_id) && 0 < intval( $quiz_id ) ) {
3124
3125
                // update pass required
3126
                if (-1 != $new_pass_required) {
3127
3128
                    $checked = $new_pass_required  ? 'on' : '';
3129
                    update_post_meta($quiz_id, '_pass_required', $checked);
3130
                    unset( $checked );
3131
                }
3132
3133
                // update pass percentage
3134
                if( !empty( $new_pass_percentage) && is_numeric( $new_pass_percentage ) ){
3135
3136
                        update_post_meta($quiz_id, '_quiz_passmark', $new_pass_percentage);
3137
3138
                }
3139
3140
                //
3141
                // update enable quiz reset
3142
                //
3143
                if (-1 != $new_enable_quiz_reset ) {
3144
3145
                    $checked = $new_enable_quiz_reset ? 'on' : ''  ;
3146
                    update_post_meta($quiz_id, '_enable_quiz_reset', $checked);
3147
                    unset( $checked );
3148
3149
                }
3150
3151
3152
            } // end if quiz
3153
3154
        }// end for each
3155
3156
        die();
3157
3158
    } // end save_all_lessons_edit_fields
3159
3160
    /**
3161
     * Loading the quick edit fields defaults.
3162
     *
3163
     * This function will localise the default values along with the script that will
3164
     * add these values to the inputs.
3165
     *
3166
     * NOTE: this function runs for each row in the edit column
3167
     *
3168
     * @since 1.8.0
3169
     * @return void
3170
     */
3171
    public function set_quick_edit_admin_defaults( $column_name, $post_id ){
3172
3173
        if( 'lesson-course' != $column_name ){
3174
            return;
3175
        }
3176
        // load the script
3177
        $suffix = defined( 'SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
3178
        wp_enqueue_script( 'sensei-lesson-quick-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-quick-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
3179
3180
        // setup the values for all meta fields
3181
        $data = array();
3182
        foreach( $this->meta_fields as $field ){
3183
3184
            $data[$field] =  get_post_meta( $post_id, '_'.$field, true );
3185
3186
        }
3187
        // add quiz meta fields
3188
        $quiz_id = Sensei()->lesson->lesson_quizzes( $post_id );
3189
        foreach( Sensei()->quiz->meta_fields as $field ){
3190
3191
            $data[$field] =  get_post_meta( $quiz_id, '_'.$field, true );
3192
3193
        }
3194
3195
        wp_localize_script( 'sensei-lesson-quick-edit', 'sensei_quick_edit_'.$post_id, $data );
3196
3197
    }// end quick edit admin defaults
3198
3199
    /**
3200
     * Filter the classes for lessons on the single course page.
3201
     *
3202
     * Adds the nesecary classes depending on the user data
3203
     *
3204
     * @since 1.9.0
3205
     * @param array $classes
3206
     * @return array $classes
3207
     */
3208
    public static function single_course_lessons_classes( $classes ){
3209
3210
        if(  is_singular('course') ){
3211
3212
            global $post;
3213
            $course_id = $post->ID;
3214
3215
            $lesson_classes = array( 'course', 'post' );
3216
            if ( is_user_logged_in() ) {
3217
3218
                // Check if Lesson is complete
3219
                $single_lesson_complete = Sensei_Utils::user_completed_lesson( get_the_ID(), get_current_user_id() );
3220
                if ( $single_lesson_complete ) {
3221
3222
                    $lesson_classes[] = 'lesson-completed';
3223
3224
                } // End If Statement
3225
3226
            } // End If Statement
3227
3228
            $is_user_taking_course = Sensei_Utils::user_started_course( $course_id, get_current_user_id() );
3229
            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...
3230
3231
                $lesson_classes[] = 'lesson-preview';
3232
3233
            }
3234
3235
            $classes = array_merge( $classes, $lesson_classes  );
3236
3237
        }
3238
3239
        return $classes;
3240
3241
    }// end single_course_lessons_classes
3242
3243
    /**
3244
     * Output the lesson meta for the given lesson
3245
     *
3246
     * @since 1.9.0
3247
     * @param $lesson_id
3248
     */
3249
    public static function the_lesson_meta( $lesson_id ){
3250
3251
        global $wp_query;
3252
        $loop_lesson_number = $wp_query->current_post + 1;
3253
3254
        $course_id = Sensei()->lesson->get_course_id( $lesson_id );
3255
        $single_lesson_complete = false;
3256
        $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 3254 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...
3257
3258
        // Get Lesson data
3259
        $complexity_array = Sensei()->lesson->lesson_complexities();
3260
3261
        $lesson_complexity = get_post_meta( $lesson_id, '_lesson_complexity', true );
3262
        if ( '' != $lesson_complexity ) {
3263
3264
            $lesson_complexity = $complexity_array[$lesson_complexity];
3265
3266
        }
3267
        $user_info = get_userdata( absint( get_post()->post_author ) );
3268
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id);
3269
        $preview_label = '';
3270
        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...
3271
3272
            $preview_label = Sensei()->frontend->sensei_lesson_preview_title_text( $lesson_id);
3273
            $preview_label = '<span class="preview-heading">' . $preview_label . '</span>';
3274
3275
        }
3276
3277
3278
        $count_markup= '';
3279
        /**
3280
         * Filter for if you want the $lesson_count to show next to the lesson.
3281
         *
3282
         * @since 1.0
3283
         * @param bool default false.
3284
         */
3285
        if( apply_filters( 'sensei_show_lesson_numbers', false ) ) {
3286
3287
            $count_markup =  '<span class="lesson-number">' . $loop_lesson_number. '</span>';
3288
3289
        }
3290
3291
        $heading_link_title = sprintf( __( 'Start %s', 'woothemes-sensei' ), get_the_title( $lesson_id ) );
3292
3293
        ?>
3294
        <header>
3295
            <h2>
3296
                <a href="<?php echo esc_url_raw( get_permalink( $lesson_id ) ) ?>"
3297
                   title="<?php esc_attr_e( $heading_link_title ) ?>" >
3298
                    <?php echo $count_markup. get_the_title( $lesson_id ) . $preview_label; ?>
3299
                </a>
3300
            </h2>
3301
3302
            <p class="lesson-meta">
3303
3304
                <?php
3305
3306
                $meta_html = '';
3307
                $user_lesson_status = Sensei_Utils::user_lesson_status( get_the_ID(), get_current_user_id() );
3308
3309
                $lesson_length = get_post_meta( $lesson_id, '_lesson_length', true );
3310 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...
3311
3312
                    $meta_html .= '<span class="lesson-length">' .  __( 'Length: ', 'woothemes-sensei' ) . $lesson_length . __( ' minutes', 'woothemes-sensei' ) . '</span>';
3313
3314
                }
3315
3316
                if ( Sensei()->settings->get( 'lesson_author' ) ) {
3317
3318
                    $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>';
3319
3320
                } // End If Statement
3321
                if ( '' != $lesson_complexity ) {
3322
3323
                    $meta_html .= '<span class="lesson-complexity">' .  __( 'Complexity: ', 'woothemes-sensei' ) . $lesson_complexity .'</span>';
3324
3325
                }
3326
3327 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...
3328
3329
                    $meta_html .= '<span class="lesson-status complete">' .__( 'Complete', 'woothemes-sensei' ) .'</span>';
3330
3331
                } elseif ( $user_lesson_status ) {
3332
3333
                    $meta_html .= '<span class="lesson-status in-progress">' . __( 'In Progress', 'woothemes-sensei' ) .'</span>';
3334
3335
                } // End If Statement
3336
3337
                echo $meta_html;
3338
3339
                ?>
3340
3341
            </p> <!-- lesson meta -->
3342
3343
        </header>
3344
3345
    <?php
3346
3347
    } // end the_lesson_meta
3348
3349
    /**
3350
     * Output the lessons thumbnail
3351
     *
3352
     * 1.9.0
3353
     *
3354
     * @param $lesson_id
3355
     */
3356
    public static function the_lesson_thumbnail( $lesson_id ){
3357
3358
        if( empty( $lesson_id ) ){
3359
3360
            $lesson_id = get_the_ID();
3361
3362
        }
3363
3364
        if( 'lesson' != get_post_type( $lesson_id ) ){
3365
            return;
3366
        }
3367
3368
        echo Sensei()->lesson->lesson_image( $lesson_id );
3369
    }
3370
3371
3372
    /**
3373
     * Alter the sensei lesson excerpt.
3374
     *
3375
     * @since 1.9.0
3376
     * @param string $excerpt
3377
     * @return string $excerpt
3378
     */
3379
    public static function alter_the_lesson_excerpt( $excerpt ) {
3380
3381
        if ('lesson' == get_post_type(get_the_ID())){
3382
3383
            // remove this hooks to avoid an infinite loop.
3384
            remove_filter( 'get_the_excerpt', array( 'WooThemes_Sensei_Lesson','alter_the_lesson_excerpt') );
3385
3386
            return WooThemes_Sensei_Lesson::lesson_excerpt( get_post( get_the_ID() ) );
3387
        }
3388
3389
        return $excerpt;
3390
3391
    }// end the_lesson_excerpt
3392
3393
    /**
3394
     * Returns the lesson prerequisite for the given lesson id.
3395
     *
3396
     * @since 1.9.0
3397
     *
3398
     * @param $current_lesson_id
3399
     * @return mixed | bool | int $prerequisite_lesson_id or false
3400
     */
3401
    public static function get_lesson_prerequisite_id( $current_lesson_id  ){
3402
3403
        $prerequisite_lesson_id = get_post_meta( $current_lesson_id , '_lesson_prerequisite', true );
3404
3405
        // set ti to false if not a valid prerequisite lesson id
3406
        if(  empty( $prerequisite_lesson_id )
3407
            || 'lesson' != get_post_type( $prerequisite_lesson_id )
3408
            || $prerequisite_lesson_id == $current_lesson_id  ) {
3409
3410
            $prerequisite_lesson_id = false;
3411
3412
        }
3413
3414
        return apply_filters( 'sensei_lesson_prerequisite', $prerequisite_lesson_id, $current_lesson_id );
3415
3416
    }
3417
3418
    /**
3419
     * This function requires that you pass in the lesson you would like to check for
3420
     * a pre-requisite and not the pre-requisite. It will check if the
3421
     * lesson has a pre-requiste and then check if it is completed.
3422
     *
3423
     * @since 1.9.0
3424
     *
3425
     * @param $lesson_id
3426
     * @param $user_id
3427
     * @return bool
3428
     */
3429
    public  static function is_prerequisite_complete( $lesson_id, $user_id  ){
3430
3431 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...
3432
        || 'lesson' != get_post_type( $lesson_id )
3433
        ||  ! is_a( get_user_by( 'id', $user_id ), 'WP_User' )){
3434
3435
            return false;
3436
3437
        }
3438
3439
        $pre_requisite_id = (string) self::get_lesson_prerequisite_id( $lesson_id );
3440
3441
        // not a valid pre-requisite so pre-requisite is completed
3442
        if( 'lesson' != get_post_type( $pre_requisite_id )
3443
            || ! is_numeric( $pre_requisite_id ) ){
3444
3445
            return true;
3446
3447
        }
3448
3449
        return  Sensei_Utils::user_completed_lesson( $pre_requisite_id, $user_id );
3450
3451
    }// end is_prerequisite_complete
3452
3453
    /**
3454
     * Show the user not taking course message if it is the case
3455
     *
3456
     * @since 1.9.0
3457
     */
3458
    public  static function user_not_taking_course_message(){
3459
3460
        $lesson_id = get_the_ID();
3461
3462
        if( 'lesson' != get_post_type( $lesson_id ) ){
3463
            return;
3464
        }
3465
3466
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id );
3467
        $pre_requisite_complete = self::is_prerequisite_complete( $lesson_id , get_current_user_id() );
3468
        $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true );
3469
        $user_taking_course = Sensei_Utils::user_started_course( $lesson_course_id, get_current_user_id() );
3470
3471
        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...
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...
3472
3473
3474
        }// end if
3475
3476
    } // end user_not_taking_course_message
3477
3478
    /**
3479
     * Outputs the lessons course signup lingk
3480
     *
3481
     * This hook runs inside the single lesson page.
3482
     *
3483
     * @since 1.9.0
3484
     */
3485
    public static function course_signup_link( ){
3486
3487
        $course_id =  Sensei()->lesson->get_course_id( get_the_ID() );
3488
3489
        if ( empty( $course_id ) || 'course' != get_post_type( $course_id ) || sensei_all_access() ) {
3490
3491
            return;
3492
3493
        }
3494
3495
        ?>
3496
3497
        <section class="course-signup lesson-meta">
3498
3499
            <?php
3500
3501
            global $current_user;
3502
            $wc_post_id = (int) get_post_meta( $course_id, '_course_woocommerce_product', true );
3503
3504
            if ( Sensei_WC::is_woocommerce_active() && Sensei_WC::is_course_purchasable( $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 3487 can also be of type boolean; however, Sensei_WC::is_course_purchasable() 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...
3505
3506
                if( is_user_logged_in() ) {
3507
3508
                    if( Sensei_WC::has_customer_bought_product( get_current_user_id(), $wc_post_id ) ) {
3509
3510
                        $prereq_course_id = get_post_meta( $course_id, '_course_prerequisite',true );
3511
                        $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>';
3512
	                    $message = sprintf( __( 'Please complete %1$s before starting the lesson.', 'woothemes-sensei' ), $course_link );
3513
	                    Sensei()->notices->add_notice( $message, 'alert' );
3514
3515 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...
3516
3517
	                    if( ! Sensei_Utils::user_started_course( $course_id, $current_user->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 3487 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...
Bug Best Practice introduced by
The expression \Sensei_Utils::user_star..._id, $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...
3518
3519
3520
		                    $a_element = '<a href="' . esc_url( get_permalink( $course_id ) ) . '" title="' . __( 'Sign Up', 'woothemes-sensei' )  . '">';
3521
		                    $a_element .= __( 'course', 'woothemes-sensei' );
3522
		                    $a_element .= '</a>';
3523
3524
		                    if( Sensei_Utils::is_preview_lesson( get_the_ID()  ) ){
3525
3526
			                    $message = sprintf( __( 'This is a preview lesson. Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3527
3528
		                    }else{
3529
3530
			                    $message = sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3531
3532
		                    }
3533
3534
		                    Sensei()->notices->add_notice( $message, 'info' );
3535
3536
	                    } // end if user started course
3537
3538
                    }// end
3539
3540 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...
3541
3542
	                $a_element = '<a href="' . esc_url( get_permalink( $course_id ) ) . '" title="' . __( 'Sign Up', 'woothemes-sensei' )  . '">';
3543
	                $a_element .= __( 'course', 'woothemes-sensei' );
3544
	                $a_element .= '</a>';
3545
3546
	                if( Sensei_Utils::is_preview_lesson( get_the_ID()  ) ){
3547
3548
						$message = sprintf( __( 'This is a preview lesson. Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3549
3550
					}else{
3551
3552
						$message = sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $a_element );
3553
3554
					}
3555
3556
					Sensei()->notices->add_notice( $message, 'alert' );
3557
3558
                 }
3559
3560
            } else { ?>
3561
3562
	            <?php if( ! Sensei_Utils::user_started_course( $course_id, get_current_user_id() ) &&  sensei_is_login_required() )  : ?>
0 ignored issues
show
Bug introduced by
It seems like $course_id defined by Sensei()->lesson->get_course_id(get_the_ID()) on line 3487 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...
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...
3563
3564
	                <div class="sensei-message alert">
3565
	                    <?php
3566
	                    $course_link =  '<a href="'
3567
	                                        . esc_url( get_permalink( $course_id ) )
3568
	                                        . '" title="' . __( 'Sign Up', 'woothemes-sensei' )
3569
	                                        . '">' . __( 'course', 'woothemes-sensei' )
3570
	                                    . '</a>';
3571
3572
						if ( Sensei_Utils::is_preview_lesson( get_the_ID( ) ) ) {
3573
3574
							echo sprintf( __( 'This is a preview lesson. Please sign up for the %1$s to access all lessons.', 'woothemes-sensei' ),  $course_link );
3575
3576
						} else {
3577
3578
							echo sprintf( __( 'Please sign up for the %1$s before starting the lesson.', 'woothemes-sensei' ),  $course_link );
3579
3580
						}
3581
3582
	                    ?>
3583
	                </div>
3584
3585
	            <?php endif; ?>
3586
3587
            <?php } // End If Statement ?>
3588
3589
        </section>
3590
3591
        <?php
3592
    }// end course_signup_link
3593
3594
    /**
3595
     * Show a message telling the user to complete the previous message if they haven't done so yet
3596
     *
3597
     * @since 1.9.0
3598
     */
3599
    public  static function prerequisite_complete_message(){
3600
3601
        $lesson_prerequisite =  WooThemes_Sensei_Lesson::get_lesson_prerequisite_id( get_the_ID() );
3602
        $lesson_has_pre_requisite = $lesson_prerequisite > 0;
3603
        if ( ! WooThemes_Sensei_Lesson::is_prerequisite_complete(  get_the_ID(), get_current_user_id() ) && $lesson_has_pre_requisite ) {
3604
3605
            $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>';
3606
            echo sprintf( __( 'You must first complete %1$s before viewing this Lesson', 'woothemes-sensei' ), $prerequisite_lesson_link );
3607
3608
        }
3609
3610
    }
3611
3612
    /**
3613
     * Deprecate the sensei_lesson_archive_header hook but keep it
3614
     * active for backwards compatibility.
3615
     *
3616
     * @deprecated since 1.9.0
3617
     */
3618
    public static function deprecate_sensei_lesson_archive_header_hook(){
3619
3620
        sensei_do_deprecated_action('sensei_lesson_archive_header', '1.9.0', 'sensei_loop_lesson_inside_before');
3621
3622
    }
3623
3624
    /**
3625
     * Outputs the the lesson archive header.
3626
     *
3627
     * @since  1.9.0
3628
     * @return void
3629
     */
3630
    public function the_archive_header( ) {
3631
3632
        $before_html = '<header class="archive-header"><h1>';
3633
        $after_html = '</h1></header>';
3634
        $html = $before_html .  __( 'Lessons Archive', 'woothemes-sensei' ) . $after_html;
3635
3636
        echo apply_filters( 'sensei_lesson_archive_title', $html );
3637
3638
    } // sensei_course_archive_header()
3639
3640
    /**
3641
     * Output the title for the single lesson page
3642
     *
3643
     * @global $post
3644
     * @since 1.9.0
3645
     */
3646
    public static function the_title(){
3647
3648
        global $post;
3649
3650
        ?>
3651
        <header>
3652
3653
            <h1>
3654
3655
                <?php
3656
                /**
3657
                 * Filter documented in class-sensei-messages.php the_title
3658
                 */
3659
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
3660
                ?>
3661
3662
            </h1>
3663
3664
        </header>
3665
3666
        <?php
3667
3668
    }//the_title
3669
3670
    /**
3671
     * Flush the rewrite rules for a lesson post type
3672
     *
3673
     * @since 1.9.0
3674
     *
3675
     * @param $post_id
3676
     */
3677 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...
3678
3679
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
3680
3681
            return;
3682
3683
        }
3684
3685
3686
        if( 'lesson' == get_post_type( $post_id )  ){
3687
3688
            Sensei()->initiate_rewrite_rules_flush();
3689
3690
        }
3691
3692
    }
3693
3694
    /**
3695
     * Output the quiz specific buttons and messaging on the single lesson page
3696
     *
3697
     *
3698
     * @since 1.0.0 moved here from frontend class
3699
     *
3700
     * @param int $lesson_id
3701
     * @param int $user_id
3702
     */
3703
    public static function footer_quiz_call_to_action( $lesson_id = 0, $user_id = 0 ) {
3704
3705
3706
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3707
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3708
3709
3710
	    if ( ! sensei_can_user_view_lesson( $lesson_id, $user_id ) ) {
3711
		    return;
3712
	    }
3713
3714
        $lesson_prerequisite       = (int) get_post_meta( $lesson_id, '_lesson_prerequisite', true );
3715
        $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...
3716
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3717
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3718
        $show_actions              = is_user_logged_in() ? true : false;
3719
3720
        if( intval( $lesson_prerequisite ) > 0 ) {
3721
3722
            // If the user hasn't completed the prereq then hide the current actions
3723
            $show_actions = Sensei_Utils::user_completed_lesson( $lesson_prerequisite, $user_id );
3724
3725
        }
3726
3727
        ?>
3728
3729
        <footer>
3730
3731
            <?php
3732
            if( $show_actions && $quiz_id && Sensei()->access_settings() ) {
3733
3734
                $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3735
                if( $has_quiz_questions ) {
3736
                    ?>
3737
3738
                    <p>
3739
3740
                        <a class="button"
3741
                           href="<?php echo esc_url_raw( get_permalink( $quiz_id ) ); ?>"
3742
                           title="<?php _e( 'View the Lesson Quiz', 'woothemes-sensei'  ); ?>">
3743
3744
                            <?php  _e( 'View the Lesson Quiz', 'woothemes-sensei' ); ?>
3745
3746
                        </a>
3747
3748
                    </p>
3749
3750
                    <?php
3751
                }
3752
3753
            } // End If Statement
3754
3755
            if ( $show_actions && ! $has_user_completed_lesson ) {
3756
3757
                sensei_complete_lesson_button();
3758
3759
            } elseif( $show_actions ) {
3760
3761
                sensei_reset_lesson_button();
3762
3763
            } // End If Statement
3764
            ?>
3765
3766
        </footer>
3767
3768
        <?php
3769
    } // End sensei_lesson_quiz_meta()
3770
3771
    /**
3772
     * Show the lesson comments. This should be used in the loop.
3773
     *
3774
     * @since 1.9.0
3775
     */
3776
    public static function output_comments(){
3777
3778
        if( ! is_user_logged_in() ){
3779
            return;
3780
        }
3781
3782
        $pre_requisite_complete = Sensei()->lesson->is_prerequisite_complete( get_the_ID(), get_current_user_id() );
3783
        $course_id = Sensei()->lesson->get_course_id( get_the_ID() );
3784
        $allow_comments = Sensei()->settings->settings[ 'lesson_comments' ];
3785
        $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 3783 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...
3786
3787
        $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...
3788
3789
        if (  $lesson_allow_comments || is_singular( 'sensei_message' ) ) {
3790
3791
            comments_template();
3792
3793
        } // End If Statement
3794
3795
    } //output_comments
3796
3797
    /**
3798
     * Display the leeson quiz status if it should be shown
3799
     *
3800
     * @param int $lesson_id defaults to the global lesson id
3801
     * @param int $user_id defaults to the current user id
3802
     *
3803
     * @since 1.9.0
3804
     */
3805
    public static function user_lesson_quiz_status_message( $lesson_id = 0, $user_id = 0){
3806
3807
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3808
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3809
        $lesson_course_id          = (int) get_post_meta( $lesson_id, '_lesson_course', true );
3810
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3811
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3812
3813
3814
        if ( $quiz_id && is_user_logged_in()
3815
            && 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...
3816
3817
            $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...
3818
            $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3819
3820
            // Display lesson quiz status message
3821
            if ( $has_user_completed_lesson || $has_quiz_questions ) {
3822
                $status = Sensei_Utils::sensei_user_quiz_status_message( $lesson_id, $user_id, true );
3823
                echo '<div class="sensei-message ' . $status['box_class'] . '">' . $status['message'] . '</div>';
3824
                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...
3825
                   // 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...
3826
                } // End If Statement
3827
            } // End If Statement
3828
3829
        }
3830
3831
    }
3832
3833
    /**
3834
     * On the lesson archive limit the number of words the show up if the access settings are enabled
3835
     *
3836
     * @since 1.9.0
3837
     * @param $content
3838
     * @return string
3839
     */
3840
    public static function limit_archive_content ( $content ){
3841
3842
        if( is_archive('lesson') && Sensei()->settings->get('access_permission') ){
3843
3844
            return wp_trim_words( $content, $num_words = 30, $more = '…' );
3845
        }
3846
3847
        return $content;
3848
3849
    } // end limit_archive_content
3850
3851
    /**
3852
     * Returns all publised lesson ID's
3853
     *
3854
     * @since 1.9.0
3855
     * @return array
3856
     */
3857
    public static function get_all_lesson_ids(){
3858
3859
        return get_posts( array(
3860
            'post_type'=>'lesson',
3861
            'fields'=>'ids',
3862
            'post_status' => 'publish',
3863
            'numberposts' => 4000, // legacy support
3864
            'post_per_page' => 4000
3865
        ));
3866
3867
    }
3868
3869
} // End Class
3870
3871
/**
3872
 * Class WooThemes_Sensei_Lesson
3873
 * @ignore only for backward compatibility
3874
 * @since 1.9.0
3875
 */
3876
class WooThemes_Sensei_Lesson extends Sensei_Lesson{}
3877