Completed
Push — master ( 0fef01...2dfc11 )
by Dwain
04:48
created

Sensei_Lesson   D

Complexity

Total Complexity 551

Size/Duplication

Total Lines 3759
Duplicated Lines 8.01 %

Coupling/Cohesion

Components 1
Dependencies 11
Metric Value
wmc 551
lcom 1
cbo 11
dl 301
loc 3759
rs 4

69 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 82 2
B meta_box_setup() 0 30 1
B lesson_info_meta_box_content() 0 28 2
B lesson_prerequisite_meta_box_content() 10 29 3
A lesson_preview_meta_box_content() 0 18 3
C meta_box_save() 17 29 11
D quiz_update() 0 109 20
B get_submitted_setting_value() 0 23 6
B save_post_meta() 5 34 6
D lesson_course_meta_box_content() 12 134 15
F quiz_panel() 21 117 10
C quiz_panel_questions() 0 48 7
F quiz_panel_question() 0 168 24
F quiz_panel_add() 15 193 21
F quiz_panel_get_existing_questions() 14 79 12
B quiz_panel_add_existing_question() 0 29 3
C quiz_panel_filter_existing_questions() 0 70 11
D quiz_panel_question_field() 39 191 33
B quiz_panel_question_feedback() 0 30 5
A question_get_answer_id() 0 9 1
A get_answer_id() 0 11 2
A lesson_quiz_meta_box_content() 0 13 2
A lesson_quiz_settings_meta_box_content() 0 21 3
A quiz_settings_panel() 0 14 3
B get_quiz_settings() 8 83 5
C enqueue_scripts() 0 37 12
B enqueue_styles() 3 13 7
A add_column_headings() 10 10 2
B add_column_data() 12 23 6
A lesson_add_course() 0 21 3
C lesson_update_question() 0 44 7
B lesson_add_multiple_questions() 0 56 8
B lesson_remove_multiple_questions() 0 23 4
A get_question_category_limit() 0 21 4
C lesson_add_existing_questions() 5 50 8
A lesson_update_grade_type() 15 15 3
B lesson_update_question_order() 0 23 5
A lesson_update_question_order_random() 15 15 3
D lesson_save_course() 3 65 14
F lesson_save_question() 8 229 69
B lesson_delete_question() 3 21 6
A lesson_complexities() 0 11 1
B lesson_count() 0 32 2
A lesson_quizzes() 18 18 1
F lesson_quiz_questions() 0 196 37
B set_default_question_order() 0 33 4
C lesson_image() 10 61 10
A lesson_excerpt() 0 13 4
C get_course_id() 0 22 8
B all_lessons_edit_fields() 0 92 5
A generate_all_lessons_edit_field() 0 13 1
C save_all_lessons_edit_fields() 0 69 16
B set_quick_edit_admin_defaults() 0 27 6
B single_course_lessons_classes() 0 34 6
D the_lesson_meta() 14 99 10
A the_lesson_thumbnail() 0 14 3
A alter_the_lesson_excerpt() 0 13 2
A get_lesson_prerequisite_id() 0 16 4
C is_prerequisite_complete() 7 23 7
B user_not_taking_course_message() 0 25 5
A prerequisite_complete_message() 0 12 3
A deprecate_sensei_lesson_archive_header_hook() 0 5 1
A the_archive_header() 0 9 1
A the_title() 0 23 1
A flush_rewrite_rules() 16 16 4
C footer_quiz_call_to_action() 0 60 12
B output_comments() 0 20 6
D user_lesson_quiz_status_message() 0 27 9
C course_signup_link() 21 82 10

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Sensei_Lesson often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Sensei_Lesson, and based on these observations, apply Extract Interface, too.

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 WordPress
10
 * @subpackage Sensei
11
 * @category Core
12
 * @author WooThemes
13
 * @since 1.0.0
14
 */
15
class Sensei_Lesson {
16
	public $token;
17
	public $meta_fields;
18
19
	/**
20
	 * Constructor.
21
	 * @since  1.0.0
22
	 */
23
	public function __construct () {
24
25
        $this->token = 'lesson';
26
27
		// Setup meta fields for this post type
28
		$this->meta_fields = array( 'lesson_prerequisite', 'lesson_course', 'lesson_preview', 'lesson_length', 'lesson_complexity', 'lesson_video_embed' );
29
30
        $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...
31
32
		// Admin actions
33
		if ( is_admin() ) {
34
35
			// Metabox functions
36
			add_action( 'admin_menu', array( $this, 'meta_box_setup' ), 20 );
37
			add_action( 'save_post', array( $this, 'meta_box_save' ) );
38
			add_action( 'save_post', array( $this, 'quiz_update' ) );
39
40
			// Custom Write Panel Columns
41
			add_filter( 'manage_edit-lesson_columns', array( $this, 'add_column_headings' ), 10, 1 );
42
			add_action( 'manage_posts_custom_column', array( $this, 'add_column_data' ), 10, 2 );
43
44
			// Add/Update question
45
			add_action( 'wp_ajax_lesson_update_question', array( $this, 'lesson_update_question' ) );
46
			add_action( 'wp_ajax_nopriv_lesson_update_question', array( $this, 'lesson_update_question' ) );
47
48
			// Add course
49
			add_action( 'wp_ajax_lesson_add_course', array( $this, 'lesson_add_course' ) );
50
			add_action( 'wp_ajax_nopriv_lesson_add_course', array( $this, 'lesson_add_course' ) );
51
52
			// Update grade type
53
			add_action( 'wp_ajax_lesson_update_grade_type', array( $this, 'lesson_update_grade_type' ) );
54
			add_action( 'wp_ajax_nopriv_lesson_update_grade_type', array( $this, 'lesson_update_grade_type' ) );
55
56
			// Update question order
57
			add_action( 'wp_ajax_lesson_update_question_order', array( $this, 'lesson_update_question_order' ) );
58
			add_action( 'wp_ajax_nopriv_lesson_update_question_order', array( $this, 'lesson_update_question_order' ) );
59
60
			//Update question order
61
			add_action( 'wp_ajax_lesson_update_question_order_random', array( $this, 'lesson_update_question_order_random' ) );
62
			add_action( 'wp_ajax_nopriv_lesson_update_question_order_random', array( $this, 'lesson_update_question_order_random' ) );
63
64
			// Get answer ID
65
			add_action( 'wp_ajax_question_get_answer_id', array( $this, 'question_get_answer_id' ) );
66
			add_action( 'wp_ajax_nopriv_question_get_answer_id', array( $this, 'question_get_answer_id' ) );
67
68
			// Add multiple questions
69
			add_action( 'wp_ajax_lesson_add_multiple_questions', array( $this, 'lesson_add_multiple_questions' ) );
70
			add_action( 'wp_ajax_nopriv_lesson_add_multiple_questions', array( $this, 'lesson_add_multiple_questions' ) );
71
72
			// Remove multiple questions
73
			add_action( 'wp_ajax_lesson_remove_multiple_questions', array( $this, 'lesson_remove_multiple_questions' ) );
74
			add_action( 'wp_ajax_nopriv_lesson_remove_multiple_questions', array( $this, 'lesson_remove_multiple_questions' ) );
75
76
			// Get question category limit
77
			add_action( 'wp_ajax_get_question_category_limit', array( $this, 'get_question_category_limit' ) );
78
			add_action( 'wp_ajax_nopriv_get_question_category_limit', array( $this, 'get_question_category_limit' ) );
79
80
			// Add existing questions
81
			add_action( 'wp_ajax_lesson_add_existing_questions', array( $this, 'lesson_add_existing_questions' ) );
82
			add_action( 'wp_ajax_nopriv_lesson_add_existing_questions', array( $this, 'lesson_add_existing_questions' ) );
83
84
			// Filter existing questions
85
			add_action( 'wp_ajax_filter_existing_questions', array( $this, 'quiz_panel_filter_existing_questions' ) );
86
			add_action( 'wp_ajax_nopriv_filter_existing_questions', array( $this, 'quiz_panel_filter_existing_questions' ) );
87
88
            // output bulk edit fields
89
            add_action( 'bulk_edit_custom_box', array( $this, 'all_lessons_edit_fields' ), 10, 2 );
90
            add_action( 'quick_edit_custom_box', array( $this, 'all_lessons_edit_fields' ), 10, 2 );
91
92
            // load quick edit default values
93
            add_action('manage_lesson_posts_custom_column', array( $this, 'set_quick_edit_admin_defaults'), 11, 2);
94
95
            // save bulk edit fields
96
            add_action( 'wp_ajax_save_bulk_edit_book', array( $this, 'save_all_lessons_edit_fields' ) );
97
98
            // flush rewrite rules when saving a lesson
99
            add_action('save_post', array( __CLASS__, 'flush_rewrite_rules' ) );
100
101
		} 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...
102
			// Frontend actions
103
		} // End If Statement
104
	} // End __construct()
105
106
	/**
107
	 * meta_box_setup function.
108
	 *
109
	 * @access public
110
	 * @return void
111
	 */
112
	public function meta_box_setup () {
113
114
		// Add Meta Box for Prerequisite Lesson
115
		add_meta_box( 'lesson-prerequisite', __( 'Lesson Prerequisite', 'woothemes-sensei' ), array( $this, 'lesson_prerequisite_meta_box_content' ), $this->token, 'side', 'default' );
116
117
		// Add Meta Box for Lesson Course
118
		add_meta_box( 'lesson-course', __( 'Lesson Course', 'woothemes-sensei' ), array( $this, 'lesson_course_meta_box_content' ), $this->token, 'side', 'default' );
119
120
		// Add Meta Box for Lesson Preview
121
		add_meta_box( 'lesson-preview', __( 'Lesson Preview', 'woothemes-sensei' ), array( $this, 'lesson_preview_meta_box_content' ), $this->token, 'side', 'default' );
122
123
		// Add Meta Box for Lesson Information
124
		add_meta_box( 'lesson-info', __( 'Lesson Information', 'woothemes-sensei' ), array( $this, 'lesson_info_meta_box_content' ), $this->token, 'normal', 'default' );
125
126
		// Add Meta Box for Quiz Settings
127
		add_meta_box( 'lesson-quiz-settings', __( 'Quiz Settings', 'woothemes-sensei' ), array( $this, 'lesson_quiz_settings_meta_box_content' ), $this->token, 'normal', 'default' );
128
129
		// Add Meta Box for Lesson Quiz Questions
130
		add_meta_box( 'lesson-quiz', __( 'Quiz Questions', 'woothemes-sensei' ), array( $this, 'lesson_quiz_meta_box_content' ), $this->token, 'normal', 'default' );
131
132
		// Remove "Custom Settings" meta box.
133
		remove_meta_box( 'woothemes-settings', $this->token, 'normal' );
134
135
		// Add JS scripts
136
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
137
138
		// Add CSS
139
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
140
141
	} // End meta_box_setup()
142
143
144
	/**
145
	 * lesson_info_meta_box_content function.
146
	 *
147
	 * @access public
148
	 * @return void
149
	 */
150
	public function lesson_info_meta_box_content () {
151
		global $post;
152
153
		$lesson_length = get_post_meta( $post->ID, '_lesson_length', true );
154
		$lesson_complexity = get_post_meta( $post->ID, '_lesson_complexity', true );
155
		$complexity_array = $this->lesson_complexities();
156
		$lesson_video_embed = get_post_meta( $post->ID, '_lesson_video_embed', true );
157
158
		$html = '';
159
		// Lesson Length
160
		$html .= '<p><label for="lesson_length">' . __( 'Lesson Length in minutes', 'woothemes-sensei' ) . ': </label>';
161
		$html .= '<input type="number" id="lesson-length" name="lesson_length" class="small-text" value="' . esc_attr( $lesson_length ) . '" /></p>' . "\n";
162
		// Lesson Complexity
163
		$html .= '<p><label for="lesson_complexity">' . __( 'Lesson Complexity', 'woothemes-sensei' ) . ': </label>';
164
		$html .= '<select id="lesson-complexity-options" name="lesson_complexity" class="chosen_select lesson-complexity-select">';
165
			$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
166
			foreach ($complexity_array as $key => $value){
167
				$html .= '<option value="' . esc_attr( $key ) . '"' . selected( $key, $lesson_complexity, false ) . '>' . esc_html( $value ) . '</option>' . "\n";
168
			} // End For Loop
169
		$html .= '</select></p>' . "\n";
170
171
		$html .= '<p><label for="lesson_video_embed">' . __( 'Video Embed Code', 'woothemes-sensei' ) . ':</label><br/>' . "\n";
172
		$html .= '<textarea rows="5" cols="50" name="lesson_video_embed" tabindex="6" id="course-video-embed">' . $lesson_video_embed . '</textarea></p>' . "\n";
173
		$html .= '<p>' .  __( 'Paste the embed code for your video (e.g. YouTube, Vimeo etc.) in the box above.', 'woothemes-sensei' ) . '</p>';
174
175
		echo $html;
176
177
	} // End lesson_info_meta_box_content()
178
179
	/**
180
	 * lesson_prerequisite_meta_box_content function.
181
	 *
182
	 * @access public
183
	 * @return void
184
	 */
185
	public function lesson_prerequisite_meta_box_content () {
186
		global $post;
187
		// Get existing post meta
188
		$select_lesson_prerequisite = get_post_meta( $post->ID, '_lesson_prerequisite', true );
189
		// Get the Lesson Posts
190
		$post_args = array(	'post_type' 		=> 'lesson',
191
							'posts_per_page' 		=> -1,
192
							'orderby'         	=> 'title',
193
    						'order'           	=> 'ASC',
194
    						'exclude' 			=> $post->ID,
195
							'suppress_filters' 	=> 0
196
							);
197
		$posts_array = get_posts( $post_args );
198
		// Build the HTML to Output
199
		$html = '';
200
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
201 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...
202
			$html .= '<select id="lesson-prerequisite-options" name="lesson_prerequisite" class="chosen_select widefat">' . "\n";
203
			$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
204
				foreach ($posts_array as $post_item){
205
					$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";
206
				} // End For Loop
207
			$html .= '</select>' . "\n";
208
		} else {
209
			$html .= '<p>' . esc_html( __( 'No lessons exist yet. Please add some first.', 'woothemes-sensei' ) ) . '</p>';
210
		} // End If Statement
211
		// Output the HTML
212
		echo $html;
213
	} // End lesson_prerequisite_meta_box_content()
214
215
	/**
216
	 * lesson_preview_meta_box_content function.
217
	 *
218
	 * @access public
219
	 * @return void
220
	 */
221
	public function lesson_preview_meta_box_content () {
222
		global $post;
223
		// Get existing post meta
224
		$lesson_preview = get_post_meta( $post->ID, '_lesson_preview', true );
225
		$html = '';
226
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
227
228
		$checked = '';
229
		if ( isset( $lesson_preview ) && ( '' != $lesson_preview ) ) {
230
	 	    $checked = checked( 'preview', $lesson_preview, false );
231
	 	} // End If Statement
232
233
	 	$html .= '<label for="lesson_preview">';
234
	 	$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>';
235
236
		// Output the HTML
237
		echo $html;
238
	} // End lesson_preview_meta_box_content()
239
240
	/**
241
	 * meta_box_save function.
242
	 *
243
	 * @access public
244
	 * @param int $post_id
245
	 * @return void
246
	 */
247
	public function meta_box_save ( $post_id ) {
248
249
		// Verify the nonce before proceeding.
250 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...
251
			return $post_id;
252
		} // End If Statement
253
		// Get the post type object.
254
		$post_type = get_post_type_object( get_post_type( $post_id ) );
255
		// Check if the current user has permission to edit the post.
256
		if ( !current_user_can( $post_type->cap->edit_post, $post_id ) ) {
257
			return $post_id;
258
		} // End If Statement
259
		// Check if the current post type is a page
260 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...
261
			if ( ! current_user_can( 'edit_page', $post_id ) ) {
262
				return $post_id;
263
			} // End If Statement
264
		} else {
265
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
266
				return $post_id;
267
			} // End If Statement
268
		} // End If Statement
269
		// Save the post meta data fields
270 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...
271
			foreach ( $this->meta_fields as $meta_key ) {
272
				$this->save_post_meta( $meta_key, $post_id );
273
			} // End For Loop
274
		} // End If Statement
275
	} // End meta_box_save()
276
277
278
	/**
279
     * Update the lesson quiz and all the post meta
280
	 *
281
	 * @access public
282
	 * @return void
283
	 */
284
	public function quiz_update( $post_id ) {
285
		global $post;
286
		// Verify the nonce before proceeding.
287
		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') ) {
288
			if ( isset($post->ID) ) {
289
				return $post->ID;
290
			} else {
291
				return false;
292
			} // End If Statement
293
		} // End If Statement
294
295
		if( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
296
			return;
297
		}
298
299
		// Temporarily disable the filter
300
        remove_action( 'save_post', array( $this, 'quiz_update' ) );
301
		// Save the Quiz
302
		$quiz_id = $this->lesson_quizzes( $post_id, 'any');
303
304
		 // Sanitize and setup the post data
305
		$_POST = stripslashes_deep( $_POST );
306
		if ( isset( $_POST[ 'quiz_id' ] ) && ( 0 < absint( $_POST[ 'quiz_id' ] ) ) ) {
307
			$quiz_id = absint( $_POST[ 'quiz_id' ] );
308
		} // End If Statement
309
		$post_title = esc_html( $_POST[ 'post_title' ] );
310
		$post_status = esc_html( $_POST[ 'post_status' ] );
311
		$post_content = '';
312
313
		// Setup Query Arguments
314
		$post_type_args = array(	'post_content' => $post_content,
315
  		    						'post_status' => $post_status,
316
  		    						'post_title' => $post_title,
317
  		    						'post_type' => 'quiz',
318
                                    'post_parent' => $post_id,
319
  		    						);
320
321
		$settings = $this->get_quiz_settings();
322
323
  		// Update or Insert the Lesson Quiz
324
		if ( 0 < $quiz_id ) {
325
			// Update the Quiz
326
			$post_type_args[ 'ID' ] = $quiz_id;
327
		    wp_update_post($post_type_args);
328
329
		    // Update the post meta data
330
		    update_post_meta( $quiz_id, '_quiz_lesson', $post_id );
331
332
		    foreach( $settings as $field ) {
333
		    	if( 'random_question_order' != $field['id'] ) {
334
			    	$value = $this->get_submitted_setting_value( $field );
335
			    	if( isset( $value ) ) {
336
			    		update_post_meta( $quiz_id, '_' . $field['id'], $value );
337
			    	}
338
			    }
339
		    }
340
341
		    // Set the post terms for quiz-type
342
		    wp_set_post_terms( $quiz_id, array( 'multiple-choice' ), 'quiz-type' );
343
		} else {
344
			// Create the Quiz
345
		    $quiz_id = wp_insert_post($post_type_args);
346
347
		    // Add the post meta data WP will add it if it doesn't exist
348
            update_post_meta( $quiz_id, '_quiz_lesson', $post_id );
349
350
		    foreach( $settings as $field ) {
351
		    	if( 'random_question_order' != $field['id'] ) {
352
353
                    //ignore values not posted to avoid
354
                    // overwriting with empty or default values
355
                    // when the values are posted from bulk edit or quick edit
356
                    if( !isset( $_POST[ $field['id'] ] ) ){
357
                        continue;
358
                    }
359
360
			    	$value = $this->get_submitted_setting_value( $field );
361
			    	if( isset( $value ) ) {
362
			    		add_post_meta( $quiz_id, '_' . $field['id'], $value );
363
			    	}
364
			    }
365
		    }
366
367
		    // Set the post terms for quiz-type
368
		    wp_set_post_terms( $quiz_id, array( 'multiple-choice' ), 'quiz-type' );
369
		} // End If Statement
370
371
		// Add default lesson order meta value
372
		$course_id = get_post_meta( $post_id, '_lesson_course', true );
373
		if( $course_id ) {
374
			if( ! get_post_meta( $post_id, '_order_' . $course_id, true ) ) {
375
				update_post_meta( $post_id, '_order_' . $course_id, 0 );
376
			}
377
		}
378
		// Add reference back to the Quiz
379
		update_post_meta( $post_id, '_lesson_quiz', $quiz_id );
380
		// Mark if the Lesson Quiz has questions
381
		$quiz_questions = Sensei()->lesson->lesson_quiz_questions( $quiz_id );
382
		if( 0 < count( $quiz_questions ) ) {
383
			update_post_meta( $post_id, '_quiz_has_questions', '1' );
384
		}
385
		else {
386
			delete_post_meta( $post_id, '_quiz_has_questions' );
387
		}
388
389
		// Restore the previously disabled filter
390
        add_action( 'save_post', array( $this, 'quiz_update' ) );
391
392
	} // End post_updated()
393
394
	public function get_submitted_setting_value( $field = false ) {
395
396
		if( ! $field ) return;
397
398
		$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...
399
400
		if( 'quiz_grade_type' == $field['id'] ) {
401
			if( isset( $_POST[ $field['id'] ] ) && 'on' == $_POST[ $field['id'] ] ) {
402
				$value = 'auto';
403
			} else {
404
				$value = 'manual';
405
			}
406
			return $value;
407
		}
408
409
		if ( isset( $_POST[ $field['id'] ] ) ) {
410
			$value = $_POST[ $field['id'] ];
411
		} else {
412
			$value = $field['default'];
413
		}
414
415
		return $value;
416
	}
417
418
	/**
419
	 * save_post_meta function.
420
	 * Saves lesson meta data
421
	 * @access private
422
	 * @param string $post_key (default: '')
423
	 * @param int $post_id (default: 0)
424
	 * @return int|bool meta id or saved status
425
	 */
426
	private function save_post_meta( $post_key = '', $post_id = 0 ) {
427
		// Get the meta key.
428
		$meta_key = '_' . $post_key;
429
430
        //ignore fields are not posted
431
432
        if( !isset( $_POST[ $post_key ] ) ){
433
434
            // except for lesson preview checkbox field
435
            if( 'lesson_preview' == $post_key ){
436
437
                $_POST[ $post_key ] = '';
438
439
            } else {
440
441
                return false;
442
443
            }
444
445
        }
446
447
		// Get the posted data and sanitize it for use as an HTML class.
448 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...
449
			$new_meta_value = esc_html( $_POST[$post_key] );
450
		} else {
451
			$new_meta_value = ( isset( $_POST[$post_key] ) ? sanitize_html_class( $_POST[$post_key] ) : '' );
452
		} // End If Statement
453
454
        // update field with the new value
455
        if( -1 != $new_meta_value  ){
456
            return update_post_meta( $post_id, $meta_key, $new_meta_value );
457
        }
458
459
	} // End save_post_meta()
460
461
	/**
462
	 * lesson_course_meta_box_content function.
463
	 *
464
	 * @access public
465
	 * @return void
466
	 */
467
	public function lesson_course_meta_box_content () {
468
		global $post;
469
		// Setup Lesson Meta Data
470
		$selected_lesson_course = 0;
471
		if ( 0 < $post->ID ) {
472
			$selected_lesson_course = get_post_meta( $post->ID, '_lesson_course', true );
473
		} // End If Statement
474
		// Handle preselected course
475 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...
476
			$selected_lesson_course = absint( $_GET[ 'course_id' ] );
477
		} // End If Statement
478
		// Get the Lesson Posts
479
		$post_args = array(	'post_type' 		=> 'course',
480
							'posts_per_page' 		=> -1,
481
							'orderby'         	=> 'title',
482
    						'order'           	=> 'ASC',
483
    						'post_status'      	=> 'any',
484
    						'suppress_filters' 	=> 0,
485
							);
486
		$posts_array = get_posts( $post_args );
487
		// Buid the HTML to Output
488
		$html = '';
489
		// Nonce
490
		$html .= wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
491
492
        // Select the course for the lesson
493
        $drop_down_args = array(
494
            'name'=>'lesson_course',
495
            'id' => 'lesson-course-options'
496
        );
497
498
        $courses = WooThemes_Sensei_Course::get_all_courses();
499
        $courses_options = array();
500
        foreach( $courses as $course ){
501
            $courses_options[ $course->ID ] = get_the_title( $course ) ;
502
        }
503
        $html .= Sensei_Utils::generate_drop_down( $selected_lesson_course, $courses_options, $drop_down_args );
504
505
        // Course Actions Panel
506
		if ( current_user_can( 'publish_courses' )) {
507
				$html .= '<div id="lesson-course-actions">';
508
					$html .= '<p>';
509
						// Add a course action link
510
						$html .= '<a id="lesson-course-add" href="#course-add" class="lesson-add-course">+ ' . __('Add New Course', 'woothemes-sensei' ) . '</a>';
511
					$html .= '</p>';
512
				$html .= '</div>';
513
				// Add a course input fields
514
				$html .= '<div id="lesson-course-details" class="hidden">';
515
					$html .= '<p>';
516
						// Course Title input
517
						$html .= '<label>' . __( 'Course Title' , 'woothemes-sensei' ) . '</label> ';
518
	  					$html .= '<input type="text" id="course-title" name="course_title" value="" size="25" class="widefat" />';
519
	  					// Course Description input
520
	  					$html .= '<label>' . __( 'Description' , 'woothemes-sensei' ) . '</label> ';
521
	  					$html .= '<textarea rows="10" cols="40" id="course-content" name="course_content" value="" size="300" class="widefat"></textarea>';
522
	  					// Course Prerequisite
523
	  					$html .= '<label>' . __( 'Course Prerequisite' , 'woothemes-sensei' ) . '</label> ';
524
	  					$html .= '<select id="course-prerequisite-options" name="course_prerequisite" class="chosen_select widefat">' . "\n";
525
							$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>';
526
							foreach ($posts_array as $post_item){
527
								$html .= '<option value="' . esc_attr( absint( $post_item->ID ) ) . '">' . esc_html( $post_item->post_title ) . '</option>' . "\n";
528
							} // End For Loop
529
						$html .= '</select>' . "\n";
530
						// Course Product
531
                        if ( Sensei_WC::is_woocommerce_active() ) {
532
	  						// Get the Products
533
							$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 526. 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...
534
535
							$product_args = array(	'post_type' 		=> array( 'product', 'product_variation' ),
536
													'posts_per_page' 		=> -1,
537
													'orderby'         	=> 'title',
538
	    											'order'           	=> 'DESC',
539
	    											'post_status'		=> array( 'publish', 'private', 'draft' ),
540
	    											'tax_query'			=> array(
541
														array(
542
															'taxonomy'	=> 'product_type',
543
															'field'		=> 'slug',
544
															'terms'		=> array( 'variable', 'grouped' ),
545
															'operator'	=> 'NOT IN'
546
														)
547
													),
548
	    											'suppress_filters' 	=> 0
549
													);
550
							$products_array = get_posts( $product_args );
551
							$html .= '<label>' . __( 'WooCommerce Product' , 'woothemes-sensei' ) . '</label> ';
552
	  						$html .= '<select id="course-woocommerce-product-options" name="course_woocommerce_product" class="chosen_select widefat">' . "\n";
553
								$html .= '<option value="-">' . __( 'None', 'woothemes-sensei' ) . '</option>';
554
								$prev_parent_id = 0;
555
								foreach ($products_array as $products_item){
556
557
									if ( 'product_variation' == $products_item->post_type ) {
558
										$product_object = get_product( $products_item->ID );
559
										$parent_id = wp_get_post_parent_id( $products_item->ID );
560
										$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...
561
									} else {
562
										$parent_id = false;
563
										$prev_parent_id = 0;
564
										$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...
565
									}
566
567
									// Show variations in groups
568 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...
569
										if( 0 != $prev_parent_id ) {
570
											$html .= '</optgroup>';
571
										}
572
										$html .= '<optgroup label="' . get_the_title( $parent_id ) . '">';
573
										$prev_parent_id = $parent_id;
574
									} elseif( ! $parent_id && 0 == $prev_parent_id ) {
575
										$html .= '</optgroup>';
576
									}
577
578
									$html .= '<option value="' . esc_attr( absint( $products_item->ID ) ) . '">' . esc_html( $products_item->post_title ) . '</option>' . "\n";
579
								} // End For Loop
580
							$html .= '</select>' . "\n";
581
						} else {
582
							// Default
583
							$html .= '<input type="hidden" name="course_woocommerce_product" id="course-woocommerce-product-options" value="-" />';
584
						}
585
						// Course Category
586
	  					$html .= '<label>' . __( 'Course Category' , 'woothemes-sensei' ) . '</label> ';
587
	  					$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' );
588
						$html .= wp_dropdown_categories(apply_filters('widget_course_categories_dropdown_args', $cat_args)) . "\n";
589
	  					// Save the course action button
590
	  					$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>';
591
						$html .= '&nbsp;&nbsp;&nbsp;';
592
						// Cancel action link
593
						$html .= '<a href="#course-add-cancel" class="lesson_course_cancel">' . __( 'Cancel', 'woothemes-sensei' ) . '</a>';
594
					$html .= '</p>';
595
				$html .= '</div>';
596
			} // End If Statement
597
598
		// Output the HTML
599
		echo $html;
600
	} // End lesson_course_meta_box_content()
601
602
	public function quiz_panel( $quiz_id = 0 ) {
603
604
		$html = wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce', true, false  );
605
		$html .= '<div id="add-quiz-main">';
606 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...
607
				$html .= '<p>';
608
					// Default message and Add a Quiz button
609
					$html .= esc_html( __( 'Once you have saved your lesson you will be able to add questions.', 'woothemes-sensei' ) );
610
				$html .= '</p>';
611
			}
612
613
			// Quiz Panel CSS Class
614
			$quiz_class = '';
615
			if ( 0 == $quiz_id ) {
616
				$quiz_class = ' class="hidden"';
617
			} // End If Statement
618
			// Build the HTML to Output
619
			$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...
620
621
			// Setup Questions Query
622
			$questions = array();
623
			if ( 0 < $quiz_id ) {
624
				$questions = $this->lesson_quiz_questions( $quiz_id );
625
			} // End If Statement
626
627
			$question_count = 0;
628 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...
629
630
				if( $question->post_type == 'multiple_question' ) {
631
					$question_number = get_post_meta( $question->ID, 'number', true );
632
					$question_count += $question_number;
633
				} else {
634
					++$question_count;
635
				}
636
637
			}
638
639
			// Inner DIV
640
			$html .= '<div id="add-quiz-metadata"' . $quiz_class . '>';
641
642
				// Quiz ID
643
				$html .= '<input type="hidden" name="quiz_id" id="quiz_id" value="' . esc_attr( $quiz_id ) . '" />';
644
645
				// Default Message
646 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...
647
					$html .= '<p class="save-note">';
648
						$html .= esc_html( __( 'Please save your lesson in order to add questions to your quiz.', 'woothemes-sensei' ) );
649
					$html .= '</p>';
650
				} // End If Statement
651
652
			$html .= '</div>';
653
654
			// Question Container DIV
655
			$html .= '<div id="add-question-main"' . $quiz_class . '>';
656
				// Inner DIV
657
				$html .= '<div id="add-question-metadata">';
658
659
					// Count of questions
660
					$html .= '<input type="hidden" name="question_counter" id="question_counter" value="' . esc_attr( $question_count ) . '" />';
661
					// Table headers
662
					$html .= '<table class="widefat" id="sortable-questions">
663
								<thead>
664
								    <tr>
665
								        <th class="question-count-column">#</th>
666
								        <th>' . __( 'Question', 'woothemes-sensei' ) . '</th>
667
								        <th style="width:45px;">' . __( 'Grade', 'woothemes-sensei' ) . '</th>
668
								        <th style="width:125px;">' . __( 'Type', 'woothemes-sensei' ) . '</th>
669
								        <th style="width:125px;">' . __( 'Action', 'woothemes-sensei' ) . '</th>
670
								    </tr>
671
								</thead>
672
								<tfoot>
673
								    <tr>
674
									    <th class="question-count-column">#</th>
675
									    <th>' . __( 'Question', 'woothemes-sensei' ) . '</th>
676
									    <th>' . __( 'Grade', 'woothemes-sensei' ) . '</th>
677
									    <th>' . __( 'Type', 'woothemes-sensei' ) . '</th>
678
									    <th>' . __( 'Action', 'woothemes-sensei' ) . '</th>
679
								    </tr>
680
								</tfoot>';
681
682
					$message_class = '';
683
					if ( 0 < $question_count ) { $message_class = 'hidden'; }
684
685
					$html .= '<tbody id="no-questions-message" class="' . esc_attr( $message_class ) . '">';
686
						$html .= '<tr>';
687
							$html .= '<td colspan="5">' . __( 'There are no Questions for this Quiz yet. Please add some below.', 'woothemes-sensei' ) . '</td>';
688
						$html .= '</tr>';
689
					$html .= '</tbody>';
690
691
					if( 0 < $question_count ) {
692
						$html .= $this->quiz_panel_questions( $questions );
693
					}
694
695
					$html .= '</table>';
696
697
					if( ! isset( $this->question_order ) ) {
698
						$this->question_order = '';
699
					}
700
701
					$html .= '<input type="hidden" id="question-order" name="question-order" value="' . $this->question_order . '" />';
702
703
				$html .= '</div>';
704
705
				// Question Action Container DIV
706
				$html .= '<div id="add-question-actions">';
707
708
					$html .= $this->quiz_panel_add();
709
710
				$html .= '</div>';
711
712
			$html .= '</div>';
713
714
		$html .= '</div>';
715
716
		return $html;
717
718
	}
719
720
	public function quiz_panel_questions( $questions = array() ) {
721
		global $quiz_questions;
722
723
		$quiz_questions = $questions;
724
725
		$html = '';
726
727
		if( count( $questions ) > 0 ) {
728
729
			$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...
730
			$question_counter = 1;
731
732
			foreach ( $questions as $question ) {
733
734
				$question_id = $question->ID;
735
736
				$question_type = Sensei()->question->get_question_type( $question_id );
737
738
				$multiple_data = array();
739
				$question_increment = 1;
740
				if( 'multiple_question' == $question->post_type ) {
741
					$question_type = 'category';
742
743
					$question_category = get_post_meta( $question->ID, 'category', true );
744
					$question_cat = get_term( $question_category, 'question-category' );
745
746
					$question_number = get_post_meta( $question->ID, 'number', true );
747
					$question_increment = $question_number;
748
749
					$multiple_data = array( $question_cat->name, $question_number );
750
				}
751
752
				if( ! $question_type ) {
753
					$question_type = 'multiple-choice';
754
				}
755
756
				// Row with question and actions
757
				$html .= $this->quiz_panel_question( $question_type, $question_counter, $question_id, 'quiz', $multiple_data );
758
				$question_counter += $question_increment;
759
760
				if( isset( $this->question_order ) && strlen( $this->question_order ) > 0 ) { $this->question_order .= ','; }
761
				$this->question_order .= $question_id;
762
			} // End For Loop
763
		}
764
765
		return $html;
766
767
	}
768
769
	public function quiz_panel_question( $question_type = '', $question_counter = 0, $question_id = 0, $context = 'quiz', $multiple_data = array() ) {
770
		global $row_counter,  $quiz_questions;
771
772
		$html = '';
773
774
		$question_class = '';
775
		if( 'quiz' == $context ) {
776
			if( ! $row_counter || ! isset( $row_counter ) ) {
777
				$row_counter = 1;
778
			}
779
			if( $row_counter % 2 ) { $question_class = 'alternate'; }
780
			++$row_counter;
781
		}
782
783
		if( $question_id ) {
784
785
			if( $question_type != 'category' ) {
786
787
				$question_grade = Sensei()->question->get_question_grade( $question_id );
788
789
				$question_media = get_post_meta( $question_id, '_question_media', true );
790
				$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...
791
				$question_media_thumb_class = $question_media_link_class = $question_media_delete_class = 'hidden';
792
				$question_media_add_button = __( 'Add file', 'woothemes-sensei' );
793
				if( 0 < intval( $question_media ) ) {
794
					$mimetype = get_post_mime_type( $question_media );
795
					if( $mimetype ) {
796
						$mimetype_array = explode( '/', $mimetype);
797
						if( isset( $mimetype_array[0] ) && $mimetype_array[0] ) {
798
							$question_media_delete_class = '';
799
							$question_media_type = $mimetype_array[0];
800
							if( 'image' == $question_media_type ) {
801
								$question_media_thumb = wp_get_attachment_thumb_url( $question_media );
802
								if( $question_media_thumb ) {
803
									$question_media_thumb_class = '';
804
								}
805
							}
806
							$question_media_url = wp_get_attachment_url( $question_media );
807
							if( $question_media_url ) {
808
								$attachment = get_post( $question_media );
809
								$question_media_title = $attachment->post_title;
810
811
								if( ! $question_media_title ) {
812
									$question_media_filename = basename( $question_media_url );
813
									$question_media_title = $question_media_filename;
814
								}
815
								$question_media_link = '<a class="' . $question_media_type . '" href="' . esc_url( $question_media_url ) . '" target="_blank">' . $question_media_title . '</a>';
816
								$question_media_link_class = '';
817
							}
818
819
							$question_media_add_button = __( 'Change file', 'woothemes-sensei' );
820
						}
821
					}
822
				}
823
824
				$random_order = get_post_meta( $question_id, '_random_order', true );
825
				if( ! $random_order ) {
826
					$random_order = 'yes';
827
				}
828
829
				if( ! $question_type ) { $question_type = 'multiple-choice'; }
830
			}
831
832
			$html .= '<tbody class="' . $question_class . '">';
833
834
				if( 'quiz' == $context ) {
835
					$html .= '<tr>';
836
						if( $question_type != 'category' ) {
837
							$question = get_post( $question_id );
838
							$html .= '<td class="table-count question-number question-count-column"><span class="number">' . $question_counter . '</span></td>';
839
							$html .= '<td>' . esc_html( $question->post_title ) . '</td>';
840
							$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...
841
							$question_types_filtered = ucwords( str_replace( array( '-', 'boolean' ), array( ' ', __( 'True/False', 'woothemes-sensei' ) ), $question_type ) );
842
							$html .= '<td>' . esc_html( $question_types_filtered ) . '</td>';
843
							$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>';
844
845
						} else {
846
847
							$end_number = intval( $question_counter ) + intval( $multiple_data[1] ) - 1;
848
							if( $question_counter == $end_number ) {
849
								$row_numbers = $question_counter;
850
							} else {
851
								$row_numbers = $question_counter . ' - ' . $end_number;
852
							}
853
							$row_title = sprintf( __( 'Selected from \'%1$s\' ', 'woothemes-sensei' ), $multiple_data[0] );
854
855
							$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>';
856
							$html .= '<td>' . esc_html( $row_title ) . '</td>';
857
							$html .= '<td class="question-grade-column"></td>';
858
							$html .= '<td><input type="hidden" name="question_id" class="row_question_id" id="question_' . $question_counter . '_id" value="' . $question_id . '" /></td>';
859
							$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>';
860
861
						}
862
					$html .= '</tr>';
863
				}
864
865
				if( $question_type != 'category' ) {
866
867
					$edit_class = '';
868
					if( 'quiz' == $context ) {
869
						$edit_class = 'hidden';
870
					}
871
872
					$question = get_post( $question_id );
873
					$html .= '<tr class="question-quick-edit ' . esc_attr( $edit_class ) . '">';
874
						$html .= '<td colspan="5">';
875
							$html .= '<span class="hidden question_original_counter">' . $question_counter . '</span>';
876
					    	$html .= '<div class="question_required_fields">';
877
878
						    	// Question title
879
						    	$html .= '<div>';
880
							    	$html .= '<label for="question_' . $question_counter . '">' . __( 'Question:', 'woothemes-sensei' ) . '</label> ';
881
							    	$html .= '<input type="text" id="question_' . $question_counter . '" name="question" value="' . esc_attr( htmlspecialchars( $question->post_title ) ) . '" size="25" class="widefat" />';
882
						    	$html .= '</div>';
883
884
						    	// Question description
885
						    	$html .= '<div>';
886
							    	$html .= '<label for="question_' . $question_counter . '_desc">' . __( 'Question Description (optional):', 'woothemes-sensei' ) . '</label> ';
887
						    	$html .= '</div>';
888
							    	$html .= '<textarea id="question_' . $question_counter . '_desc" name="question_description" class="widefat" rows="4">' . esc_textarea( $question->post_content ) . '</textarea>';
889
890
						    	// Question grade
891
						    	$html .= '<div>';
892
							    	$html .= '<label for="question_' . $question_counter . '_grade">' . __( 'Question grade:', 'woothemes-sensei' ) . '</label> ';
893
							    	$html .= '<input type="number" id="question_' . $question_counter . '_grade" class="question_grade small-text" name="question_grade" min="0" value="' . $question_grade . '" />';
894
						    	$html .= '</div>';
895
896
						    	// Random order
897
						    	if( $question_type == 'multiple-choice' ) {
898
						    		$html .= '<div>';
899
						    			$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...
900
						    		$html .= '</div>';
901
						    	}
902
903
						    	// Question media
904
						    	$html .= '<div>';
905
							    	$html .= '<label for="question_' . $question_counter . '_media_button">' . __( 'Question media:', 'woothemes-sensei' ) . '</label><br/>';
906
							    	$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...
907
							    	$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...
908
							    	$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...
909
							    	$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...
910
							    	$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...
911
						    	$html .= '</div>';
912
913
						    $html .= '</div>';
914
915
						    $html .= $this->quiz_panel_question_field( $question_type, $question_id, $question_counter );
916
917
						    $html .= '<input type="hidden" id="question_' . $question_counter . '_question_type" class="question_type" name="question_type" value="' . $question_type . '" />';
918
							$html .= '<input type="hidden" name="question_id" class="row_question_id" id="question_' . $question_counter . '_id" value="' . $question_id . '" />';
919
920
							if( 'quiz' == $context ) {
921
					    		$html .= '<div class="update-question">';
922
						    		$html .= '<a href="#question-edit-cancel" class="lesson_question_cancel" title="' . esc_attr( __( 'Cancel', 'woothemes-sensei' ) ) . '">' . __( 'Cancel', 'woothemes-sensei' ) . '</a> ';
923
						    		$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>';
924
					    		$html .= '</div>';
925
					    	}
926
927
			    		$html .= '</td>';
928
					$html .= '</tr>';
929
				}
930
931
			$html .= '</tbody>';
932
933
		}
934
935
		return $html;
936
	}
937
938
	public function quiz_panel_add( $context = 'quiz' ) {
939
940
941
		$html = '<div id="add-new-question">';
942
943
			$question_types = Sensei()->question->question_types();
944
945
			$question_cats = get_terms( 'question-category', array( 'hide_empty' => false ) );
946
947
			if( 'quiz' == $context ) {
948
	    		$html .= '<h2 class="nav-tab-wrapper add-question-tabs">';
949
	    			$html .= '<a id="tab-new" class="nav-tab nav-tab-active">' . __( 'New Question'  , 'woothemes-sensei' ) . '</a>';
950
	    			$html .= '<a id="tab-existing" class="nav-tab">' . __( 'Existing Questions'  , 'woothemes-sensei' ) . '</a>';
951
                    if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats )  && ! Sensei()->teacher->is_admin_teacher() ) {
952
	    				$html .= '<a id="tab-multiple" class="nav-tab">' . __( 'Category Questions'  , 'woothemes-sensei' ) . '</a>';
953
	    			}
954
	    		$html .= '</h2>';
955
	    	}
956
957
	    	$html .= '<div class="tab-content" id="tab-new-content">';
958
959
	    		if( 'quiz' == $context ) {
960
	    			$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>';
961
	    		}
962
963
				$html .= '<div class="question">';
964
					$html .= '<div class="question_required_fields">';
965
966
						// Question title
967
						$html .= '<p><label>' . __( 'Question:'  , 'woothemes-sensei' ) . '</label> ';
968
	  					$html .= '<input type="text" id="add_question" name="question" value="" size="25" class="widefat" /></p>';
969
970
						// Question description
971
						$html .= '<p>';
972
							$html .= '<label for="question_desc">' . __( 'Question Description (optional):', 'woothemes-sensei' ) . '</label> ';
973
						$html .= '</p>';
974
						$html .= '<textarea id="question_desc" name="question_description" class="widefat" rows="4"></textarea>';
975
976
	  					// Question type
977
						$html .= '<p><label>' . __( 'Question Type:' , 'woothemes-sensei' ) . '</label> ';
978
						$html .= '<select id="add-question-type-options" name="question_type" class="chosen_select widefat question-type-select">' . "\n";
979 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...
980
								$html .= '<option value="' . esc_attr( $type ) . '">' . esc_html( $label ) . '</option>' . "\n";
981
							} // End For Loop
982
						$html .= '</select></p>' . "\n";
983
984
						// Question category
985
						if( 'quiz' == $context ) {
986
							if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats ) ) {
987
								$html .= '<p><label>' . __( 'Question Category:' , 'woothemes-sensei' ) . '</label> ';
988
								$html .= '<select id="add-question-category-options" name="question_category" class="chosen_select widefat question-category-select">' . "\n";
989
								$html .= '<option value="">' . __( 'None', 'woothemes-sensei' ) . '</option>' . "\n";
990 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...
991
									$html .= '<option value="' . esc_attr( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
992
								} // End For Loop
993
								$html .= '</select></p>' . "\n";
994
							}
995
						}
996
997
	  					// Question grade
998
						$html .= '<p><label>' . __( 'Question Grade:'  , 'woothemes-sensei' ) . '</label> ';
999
						$html .= '<input type="number" id="add-question-grade" name="question_grade" class="small-text" min="0" value="1" /></p>' . "\n";
1000
1001
						// Random order
1002
						$html .= '<p class="add_question_random_order">';
1003
			    			$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>';
1004
			    		$html .= '</p>';
1005
1006
			    		// Question media
1007
						$html .= '<p>';
1008
					    	$html .= '<label for="question_add_new_media_button">' . __( 'Question media:', 'woothemes-sensei' ) . '</label><br/>';
1009
					    	$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>';
1010
					    	$html .= '<button id="question_add_new_media_button_delete" class="delete_media_file_button button-secondary hidden">' . __( 'Delete file', 'woothemes-sensei' ) . '</button><br/>';
1011
					    	$html .= '<span id="question_add_new_media_link" class="question_media_link hidden"></span>';
1012
					    	$html .= '<br/><img id="question_add_new_media_preview" class="question_media_preview hidden" src="" /><br/>';
1013
					    	$html .= '<input type="hidden" id="question_add_new_media" class="question_media" name="question_media" value="" />';
1014
				    	$html .= '</p>';
1015
1016
					$html .= '</div>';
1017
				$html .= '</div>';
1018
1019
				foreach ( $question_types as $type => $label ) {
1020
					$html .= $this->quiz_panel_question_field( $type );
1021
				}
1022
1023
				if( 'quiz' == $context ) {
1024
					$html .= '<div class="add-question">';
1025
			    		$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>';
1026
		    		$html .= '</div>';
1027
		    	}
1028
1029
		    $html .= '</div>';
1030
1031
		    if( 'quiz' == $context ) {
1032
1033
			    $html .= '<div class="tab-content hidden" id="tab-existing-content">';
1034
1035
			    	$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>';
1036
1037
			    	$html .= '<div id="existing-filters" class="alignleft actions">
1038
			    				<select id="existing-status">
1039
			    					<option value="all">' . __( 'All', 'woothemes-sensei' ) . '</option>
1040
			    					<option value="unused">' . __( 'Unused', 'woothemes-sensei' ) . '</option>
1041
			    					<option value="used">' . __( 'Used', 'woothemes-sensei' ) . '</option>
1042
			    				</select>
1043
			    				<select id="existing-type">
1044
			    					<option value="">' . __( 'All Types', 'woothemes-sensei' ) . '</option>';
1045 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...
1046
										$html .= '<option value="' . esc_attr( $type ) . '">' . esc_html( $label ) . '</option>';
1047
									}
1048
    				$html .= '</select>
1049
    							<select id="existing-category">
1050
			    					<option value="">' . __( 'All Categories', 'woothemes-sensei' ) . '</option>';
1051 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...
1052
										$html .= '<option value="' . esc_attr( $cat->slug ) . '">' . esc_html( $cat->name ) . '</option>';
1053
									}
1054
    				$html .= '</select>
1055
    							<input type="text" id="existing-search" placeholder="' . __( 'Search', 'woothemes-sensei' ) . '" />
1056
    							<a class="button" id="existing-filter-button">' . __( 'Filter', 'woothemes-sensei' ) . '</a>
1057
			    			</div>';
1058
1059
			    	$html .= '<table id="existing-table" class="widefat">';
1060
1061
			    		$html .= '<thead>
1062
									    <tr>
1063
									        <th scope="col" class="column-cb check-column"><input type="checkbox" /></th>
1064
									        <th scope="col">' . __( 'Question', 'woothemes-sensei' ) . '</th>
1065
									        <th scope="col">' . __( 'Type', 'woothemes-sensei' ) . '</th>
1066
									        <th scope="col">' . __( 'Category', 'woothemes-sensei' ) . '</th>
1067
									    </tr>
1068
									</thead>
1069
									<tfoot>
1070
									    <tr>
1071
										    <th scope="col" class="check-column"><input type="checkbox" /></th>
1072
									        <th scope="col">' . __( 'Question', 'woothemes-sensei' ) . '</th>
1073
									        <th scope="col">' . __( 'Type', 'woothemes-sensei' ) . '</th>
1074
									        <th scope="col">' . __( 'Category', 'woothemes-sensei' ) . '</th>
1075
									    </tr>
1076
									</tfoot>';
1077
						$html .= '<tbody id="existing-questions">';
1078
1079
						$questions = $this->quiz_panel_get_existing_questions();
1080
1081
						$row = 1;
1082
						foreach( $questions['questions'] as $question ) {
1083
							$html .= $this->quiz_panel_add_existing_question( $question->ID, $row );
1084
							++$row;
1085
						}
1086
1087
						$html .= '</tbody>';
1088
1089
			    	$html .= '</table>';
1090
1091
			    	$next_class = '';
1092
			    	if( $questions['count'] <= 10 ) {
1093
			    		$next_class = 'hidden';
1094
			    	}
1095
1096
			    	$html .= '<div id="existing-pagination">';
1097
			    		$html .= '<input type="hidden" id="existing-page" value="1" />';
1098
			    		$html .= '<a class="prev no-paging">&larr; ' . __( 'Previous', 'woothemes-sensei') . '</a> <a class="next ' . esc_attr( $next_class ) . '">' . __( 'Next', 'woothemes-sensei') . ' &rarr;</a>';
1099
			    	$html .= '</div>';
1100
1101
			    	$html .= '<div class="existing-actions">';
1102
			    		$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>';
1103
			    	$html .= '</div>';
1104
1105
			    $html .= '</div>';
1106
1107
			    if ( ! empty( $question_cats ) && ! is_wp_error( $question_cats ) ) {
1108
				    $html .= '<div class="tab-content hidden" id="tab-multiple-content">';
1109
1110
				    	$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>';
1111
1112
						$html .= '<p><select id="add-multiple-question-category-options" name="multiple_category" class="chosen_select widefat question-category-select">' . "\n";
1113
						$html .= '<option value="">' . __( 'Select a Question Category', 'woothemes-sensei' ) . '</option>' . "\n";
1114 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...
1115
							$html .= '<option value="' . esc_attr( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
1116
						} // End For Loop
1117
						$html .= '</select></p>' . "\n";
1118
1119
						$html .= '<p>' . __( 'Number of questions:', 'woothemes-sensei' ) . ' <input type="number" min="1" value="1" max="1" id="add-multiple-question-count" class="small-text"/>';
1120
1121
						$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>';
1122
1123
				    $html .= '</div>';
1124
				}
1125
			}
1126
1127
		$html .= '</div>';
1128
1129
		return $html;
1130
	}
1131
1132
	public function quiz_panel_get_existing_questions( $question_status = 'all', $question_type = '', $question_category = '', $question_search = '', $page = 1 ) {
1133
1134
		$args = array(
1135
			'post_type' => 'question',
1136
			'posts_per_page' => 10,
1137
			'post_status' => 'publish',
1138
			'suppress_filters' => 0,
1139
		);
1140
1141
		switch( $question_status ) {
1142
			case 'unused': $quiz_status = 'NOT EXISTS'; break;
1143
			case 'used': $quiz_status = 'EXISTS'; break;
1144
			default: $quiz_status = ''; break;
1145
		}
1146
1147
		if( $quiz_status ) {
1148
			switch( $quiz_status ) {
1149
				case 'EXISTS':
1150
					$args['meta_query'][] = array(
1151
						'key' => '_quiz_id',
1152
						'compare' => $quiz_status,
1153
					);
1154
				break;
1155
1156
				case 'NOT EXISTS':
1157
					$args['meta_query'][] = array(
1158
						'key' => '_quiz_id',
1159
						'value' => 'bug #23268',
1160
						'compare' => $quiz_status,
1161
					);
1162
				break;
1163
			}
1164
		}
1165
1166 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...
1167
			$args['tax_query'][] = array(
1168
				'taxonomy' => 'question-type',
1169
				'field' => 'slug',
1170
				'terms' => $question_type,
1171
			);
1172
		}
1173
1174 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...
1175
			$args['tax_query'][] = array(
1176
				'taxonomy' => 'question-category',
1177
				'field' => 'slug',
1178
				'terms' => $question_category,
1179
			);
1180
		}
1181
1182
		if( $question_type && $question_category ) {
1183
			$args['tax_query']['relation'] = 'AND';
1184
		}
1185
1186
		if( $question_search ) {
1187
			$args['s'] = $question_search;
1188
		}
1189
1190
		if( $page ) {
1191
			$args['paged'] = $page;
1192
		}
1193
1194
		$qry = new WP_Query( $args );
1195
1196
        /**
1197
         * Filter existing questions query
1198
         *
1199
         * @since 1.8.0
1200
         *
1201
         * @param WP_Query $wp_query
1202
         */
1203
        $qry = apply_filters( 'sensei_existing_questions_query_results', $qry );
1204
1205
		$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...
1206
		$questions['count'] = intval( $qry->found_posts );
1207
		$questions['page'] = $page;
1208
1209
		return $questions;
1210
	}
1211
1212
	public function quiz_panel_add_existing_question( $question_id = 0, $row = 1 ) {
1213
1214
		$html = '';
1215
1216
		if( ! $question_id ) {
1217
1218
            return;
1219
1220
        }
1221
1222
		$existing_class = '';
1223
		if( $row % 2 ) {
1224
            $existing_class = 'alternate';
1225
        }
1226
1227
		$question_type = Sensei()->question->get_question_type( $question_id );
1228
1229
		$question_cat_list = strip_tags( get_the_term_list( $question_id, 'question-category', '', ', ', '' ) );
1230
1231
		$html .= '<tr class="' . esc_attr( $existing_class ) . '">
1232
					<td class="cb"><input type="checkbox" value="' . $question_id . '" class="existing-item" /></td>
1233
					<td>' . get_the_title( $question_id ) . '</td>
1234
					<td>' . esc_html( $question_type ) . '</td>
1235
					<td>' . esc_html( $question_cat_list ) . '</td>
1236
				  </tr>';
1237
1238
		return $html;
1239
1240
	}
1241
1242
	public function quiz_panel_filter_existing_questions() {
1243
1244
		$return = '';
1245
1246
		//Add nonce security to the request
1247
		$nonce = '';
1248
		if( isset( $_POST['filter_existing_questions_nonce'] ) ) {
1249
			$nonce = esc_html( $_POST['filter_existing_questions_nonce'] );
1250
		} // End If Statement
1251
1252
		if( ! wp_verify_nonce( $nonce, 'filter_existing_questions_nonce' ) ) {
1253
			die( $return );
1254
		} // End If Statement
1255
1256
		// Parse POST data
1257
		$data = $_POST['data'];
1258
		$question_data = array();
1259
		parse_str( $data, $question_data );
1260
1261
		if( 0 < count( $question_data ) ) {
1262
1263
			$question_status = '';
1264
			if( isset( $question_data['question_status'] ) ) {
1265
				$question_status = $question_data['question_status'];
1266
			}
1267
1268
			$question_type = '';
1269
			if( isset( $question_data['question_type'] ) ) {
1270
				$question_type = $question_data['question_type'];
1271
			}
1272
1273
			$question_category = '';
1274
			if( isset( $question_data['question_category'] ) ) {
1275
				$question_category = $question_data['question_category'];
1276
			}
1277
1278
			$question_search = '';
1279
			if( isset( $question_data['question_search'] ) ) {
1280
				$question_search = $question_data['question_search'];
1281
			}
1282
1283
			$question_page = 1;
1284
			if( isset( $question_data['question_page'] ) ) {
1285
				$question_page = intval( $question_data['question_page'] );
1286
			}
1287
1288
			$questions = $this->quiz_panel_get_existing_questions( $question_status, $question_type, $question_category, $question_search, $question_page );
1289
1290
			$row = 1;
1291
			$html = '';
1292
			foreach( $questions['questions'] as $question ) {
1293
				$html .= $this->quiz_panel_add_existing_question( $question->ID, $row );
1294
				++$row;
1295
			}
1296
1297
			if( ! $html ) {
1298
				$html = '<tr class="alternate">
1299
								<td class="no-results" colspan="4"><em>' . __( 'There are no questions matching your search.', 'woothemes-sensei' ) . '</em></td>
1300
							  </tr>';
1301
			}
1302
1303
			$return['html'] = $html;
1304
			$return['count'] = $questions['count'];
1305
			$return['page'] = $questions['page'];
1306
1307
			wp_send_json( $return );
1308
		}
1309
1310
		die( $return );
1311
	}
1312
1313
	public function quiz_panel_question_field( $question_type = '', $question_id = 0, $question_counter = 0 ) {
1314
1315
		$html = '';
1316
1317
		if( $question_type ) {
1318
1319
			$right_answer = '';
1320
			$wrong_answers = array();
1321
			$answer_order_string = '';
1322
			$answer_order = array();
1323
			if( $question_id ) {
1324
				$right_answer = get_post_meta( $question_id, '_question_right_answer', true);
1325
				$wrong_answers = get_post_meta( $question_id, '_question_wrong_answers', true);
1326
				$answer_order_string = get_post_meta( $question_id, '_answer_order', true );
1327
				$answer_order = array_filter( explode( ',', $answer_order_string ) );
1328
				$question_class = '';
1329
			} else {
1330
				$question_id = '';
1331
				$question_class = 'answer-fields question_required_fields hidden';
1332
			}
1333
1334
			switch ( $question_type ) {
1335
				case 'multiple-choice':
1336
					$html .= '<div class="question_default_fields multiple-choice-answers ' . str_replace( ' hidden', '', $question_class ) . '">';
1337
1338
						$right_answers = (array) $right_answer;
1339
						// Calculate total right answers available (defaults to 1)
1340
						$total_right = 0;
1341
						if( $question_id ) {
1342
							$total_right = get_post_meta( $question_id, '_right_answer_count', true );
1343
						}
1344
						if( 0 == intval( $total_right ) ) {
1345
							$total_right = 1;
1346
						}
1347
						for ( $i = 0; $i < $total_right; $i++ ) {
1348
							if ( !isset( $right_answers[ $i ] ) ) { $right_answers[ $i ] = ''; }
1349
							$right_answer_id = $this->get_answer_id( $right_answers[ $i ] );
1350
							// Right Answer
1351
							$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>';
1352
							if( $question_id ) {
1353
								$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...
1354
							} else {
1355
								$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...
1356
							}
1357
						}
1358
1359
				    	// Calculate total wrong answers available (defaults to 4)
1360
				    	$total_wrong = 0;
1361
				    	if( $question_id ) {
1362
				    		$total_wrong = get_post_meta( $question_id, '_wrong_answer_count', true );
1363
				    	}
1364
				    	if( 0 == intval( $total_wrong ) ) {
1365
				    		$total_wrong = 1;
1366
				    	}
1367
1368
                        // Setup Wrong Answer HTML
1369
                        foreach ( $wrong_answers as $i => $answer ){
1370
1371
                            $answer_id = $this->get_answer_id( $answer );
1372
                            $wrong_answer = '<label class="answer" for="question_' . $question_counter . '_wrong_answer_' . $i . '"><span>' . __( 'Wrong:' , 'woothemes-sensei' ) ;
1373
                            $wrong_answer .= '</span> <input rel="' . esc_attr( $answer_id ) . '" type="text" id="question_' . $question_counter . '_wrong_answer_' . $i ;
1374
                            $wrong_answer .= '" name="question_wrong_answers[]" value="' . esc_attr( $answer ) . '" size="25" class="question_answer widefat" /> <a class="remove_answer_option"></a></label>';
1375
                            if( $question_id ) {
1376
1377
                                $answers[ $answer_id ] = $wrong_answer;
1378
1379
                            } else {
1380
1381
                                $answers[] = $wrong_answer;
1382
1383
                            }
1384
1385
                        } // end for each
1386
1387
				    	$answers_sorted = $answers;
1388 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...
1389
				    		$answers_sorted = array();
1390
				    		foreach( $answer_order as $answer_id ) {
1391
				    			if( isset( $answers[ $answer_id ] ) ) {
1392
				    				$answers_sorted[ $answer_id ] = $answers[ $answer_id ];
1393
				    				unset( $answers[ $answer_id ] );
1394
				    			}
1395
				    		}
1396
1397
				    		if( count( $answers ) > 0 ) {
1398
						    	foreach( $answers as $id => $answer ) {
1399
						    		$answers_sorted[ $id ] = $answer;
1400
						    	}
1401
						    }
1402
				    	}
1403
1404
						foreach( $answers_sorted as $id => $answer ) {
1405
				    		$html .= $answer;
1406
				    	}
1407
1408
				    	$html .= '<input type="hidden" class="answer_order" name="answer_order" value="' . $answer_order_string . '" />';
1409
				    	$html .= '<span class="hidden right_answer_count">' . $total_right . '</span>';
1410
				    	$html .= '<span class="hidden wrong_answer_count">' . $total_wrong . '</span>';
1411
1412
				    	$html .= '<div class="add_answer_options">';
1413
					    	$html .= '<a class="add_right_answer_option add_answer_option button" rel="' . $question_counter . '">' . __( 'Add right answer', 'woothemes-sensei' ) . '</a>';
1414
					    	$html .= '<a class="add_wrong_answer_option add_answer_option button" rel="' . $question_counter . '">' . __( 'Add wrong answer', 'woothemes-sensei' ) . '</a>';
1415
				    	$html .= '</div>';
1416
1417
                        $html .= $this->quiz_panel_question_feedback( $question_counter, $question_id , 'multiple-choice' );
1418
1419
			    	$html .= '</div>';
1420
				break;
1421
				case 'boolean':
1422
					$html .= '<div class="question_boolean_fields ' . $question_class . '">';
1423
						if( $question_id ) {
1424
							$field_name = 'question_' . $question_id . '_right_answer_boolean';
1425
						} else {
1426
							$field_name = 'question_right_answer_boolean';
1427
							$right_answer = 'true';
1428
						}
1429
						$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>';
1430
						$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>';
1431
1432
                    $html .= $this->quiz_panel_question_feedback( $question_counter, $question_id, 'boolean' );
1433
1434
					$html .= '</div>';
1435
				break;
1436
				case 'gap-fill':
1437
					$gapfill_array = explode( '||', $right_answer );
1438
					if ( isset( $gapfill_array[0] ) ) { $gapfill_pre = $gapfill_array[0]; } else { $gapfill_pre = ''; }
1439
					if ( isset( $gapfill_array[1] ) ) { $gapfill_gap = $gapfill_array[1]; } else { $gapfill_gap = ''; }
1440
					if ( isset( $gapfill_array[2] ) ) { $gapfill_post = $gapfill_array[2]; } else { $gapfill_post = ''; }
1441
					$html .= '<div class="question_gapfill_fields ' . $question_class . '">';
1442
						// Fill in the Gaps
1443
						$html .= '<label>' . __( 'Text before the Gap:' , 'woothemes-sensei' ) . '</label> ';
1444
						$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" />';
1445
	  					$html .= '<label>' . __( 'The Gap:' , 'woothemes-sensei' ) . '</label> ';
1446
	  					$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" />';
1447
	  					$html .= '<label>' . __( 'Text after the Gap:' , 'woothemes-sensei' ) . '</label> ';
1448
	  					$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" />';
1449
	  					$html .= '<label>' . __( 'Preview:' , 'woothemes-sensei' ) . '</label> ';
1450
	  					$html .= '<p class="gapfill-preview">' . $gapfill_pre . '&nbsp;<u>' . $gapfill_gap . '</u>&nbsp;' . $gapfill_post . '</p>';
1451
	  				$html .= '</div>';
1452
				break;
1453 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...
1454
					$html .= '<div class="question_multiline_fields ' . $question_class . '">';
1455
						// Guides for grading
1456
						if( $question_counter ) {
1457
							$field_id = 'question_' . $question_counter . '_add_question_right_answer_multiline';
1458
						} else {
1459
							$field_id = 'add_question_right_answer_multiline';
1460
						}
1461
						$html .= '<label>' . __( 'Guide/Teacher Notes for grading the answer' , 'woothemes-sensei' ) . '</label> ';
1462
						$html .= '<textarea id="' . $field_id . '" name="add_question_right_answer_multiline" rows="4" cols="40" class="widefat">' . $right_answer . '</textarea>';
1463
					$html .= '</div>';
1464
				break;
1465 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...
1466
					$html .= '<div class="question_singleline_fields ' . $question_class . '">';
1467
						// Recommended Answer
1468
						if( $question_counter ) {
1469
							$field_id = 'question_' . $question_counter . '_add_question_right_answer_singleline';
1470
						} else {
1471
							$field_id = 'add_question_right_answer_singleline';
1472
						}
1473
						$html .= '<label>' . __( 'Recommended Answer' , 'woothemes-sensei' ) . '</label> ';
1474
						$html .= '<input type="text" id="' . $field_id . '" name="add_question_right_answer_singleline" value="' . $right_answer . '" size="25" class="widefat" />';
1475
					$html .= '</div>';
1476
				break;
1477
				case 'file-upload':
1478
					$html .= '<div class="question_fileupload_fields ' . $question_class . '">';
1479
						if( $question_counter ) {
1480
							$right_field_id = 'question_' . $question_counter . '_add_question_right_answer_fileupload';
1481
							$wrong_field_id = 'question_' . $question_counter . '_add_question_wrong_answer_fileupload';
1482
						} else {
1483
							$right_field_id = 'add_question_right_answer_fileupload';
1484
							$wrong_field_id = 'add_question_wrong_answer_fileupload';
1485
						}
1486
1487
						$wrong_answer = '';
1488
						if( isset( $wrong_answers[0] ) ) {
1489
							$wrong_answer = $wrong_answers[0];
1490
						}
1491
						$html .= '<label>' . __( 'Description for student explaining what needs to be uploaded' , 'woothemes-sensei' ) . '</label> ';
1492
						$html .= '<textarea id="' . $wrong_field_id . '" name="add_question_wrong_answer_fileupload" rows="4" cols="40" class="widefat">' . $wrong_answer . '</textarea>';
1493
1494
						// Guides for grading
1495
						$html .= '<label>' . __( 'Guide/Teacher Notes for grading the upload' , 'woothemes-sensei' ) . '</label> ';
1496
						$html .= '<textarea id="' . $right_field_id . '" name="add_question_right_answer_fileupload" rows="4" cols="40" class="widefat">' . $right_answer . '</textarea>';
1497
					$html .= '</div>';
1498
				break;
1499
			}
1500
		}
1501
1502
		return $html;
1503
	}
1504
1505
	public function quiz_panel_question_feedback( $question_counter = 0, $question_id = 0, $question_type = '' ) {
1506
1507
        // default field name
1508
        $field_name = 'answer_feedback';
1509
        if( 'boolean' == $question_type ){
1510
1511
            $field_name = 'answer_feedback_boolean';
1512
1513
        }elseif( 'multiple-choice' == $question_type ){
1514
1515
            $field_name = 'answer_feedback_multiple_choice';
1516
1517
        }// end if
1518
1519
		if( $question_counter ) {
1520
			$field_name = 'answer_' . $question_counter . '_feedback';
1521
		}
1522
1523
		$feedback = '';
1524
		if( $question_id ) {
1525
			$feedback = get_post_meta( $question_id, '_answer_feedback', true );
1526
		}
1527
1528
		$html = '<p title="' . __( 'This feedback will be automatically displayed to the student once they have completed the quiz.', 'woothemes-sensei' ) . '">';
1529
		$html .= '<label for="' . $field_name . '">' . __( 'Answer Feedback' , 'woothemes-sensei' ) . ':</label>';
1530
		$html .= '<textarea id="' . $field_name . '" name="' . $field_name . '" rows="4" cols="40" class="answer_feedback widefat">' . $feedback . '</textarea>';
1531
		$html .= '</p>';
1532
1533
		return $html;
1534
	}
1535
1536
	public function question_get_answer_id() {
1537
		$data = $_POST['data'];
1538
		$answer_data = array();
1539
		parse_str( $data, $answer_data );
1540
		$answer = $answer_data['answer_value'];
1541
		$answer_id = $this->get_answer_id( $answer );
1542
		echo $answer_id;
1543
		die();
1544
	}
1545
1546
	public function get_answer_id( $answer = '' ) {
1547
1548
		$answer_id = '';
1549
1550
		if( $answer ) {
1551
			$answer_id = md5( $answer );
1552
		}
1553
1554
		return $answer_id;
1555
1556
	}
1557
1558
	/**
1559
	 * lesson_quiz_meta_box_content function.
1560
	 *
1561
	 * @access public
1562
	 * @return void
1563
	 */
1564
	public function lesson_quiz_meta_box_content () {
1565
		global $post;
1566
1567
		// Get quiz panel
1568
		$quiz_id = 0;
1569
		$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...
1570
		if ( 0 < $post->ID ) {
1571
			$quiz_id = $this->lesson_quizzes( $post->ID, 'any' );
1572
		}
1573
1574
		echo $this->quiz_panel( $quiz_id );
1575
1576
	} // End lesson_quiz_meta_box_content()
1577
1578
	/**
1579
	 * Quiz settings metabox
1580
	 * @return void
1581
	 */
1582
	public function lesson_quiz_settings_meta_box_content() {
1583
		global $post;
1584
1585
		$html = '';
1586
1587
		// Get quiz panel
1588
		$quiz_id = 0;
1589
		$lesson_id = $post->ID;
1590
		$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...
1591
		if ( 0 < $lesson_id ) {
1592
			$quiz_id = $this->lesson_quizzes( $lesson_id, 'any' );
1593
		}
1594
1595
		if( $quiz_id ) {
1596
			$html .= $this->quiz_settings_panel( $lesson_id, $quiz_id );
1597
		} else {
1598
			$html .= '<p><em>' . __( 'There is no quiz for this lesson yet - please add one in the \'Quiz Questions\' box.', 'woothemes-sensei' ) . '</em></p>';
1599
		}
1600
1601
		echo $html;
1602
	}
1603
1604
	public function quiz_settings_panel( $lesson_id = 0, $quiz_id = 0 ) {
1605
1606
1607
		$html = '';
1608
1609
		if( ! $lesson_id && ! $quiz_id ) return $html;
1610
1611
		$settings = $this->get_quiz_settings( $quiz_id );
1612
1613
		$html = Sensei()->admin->render_settings( $settings, $quiz_id, 'quiz-settings' );
1614
1615
		return $html;
1616
1617
	}
1618
1619
	public function get_quiz_settings( $quiz_id = 0 ) {
1620
1621
		$disable_passmark = '';
1622
		$pass_required = get_post_meta( $quiz_id, '_pass_required', true );
1623
		if( ! $pass_required ) {
1624
			$disable_passmark = 'hidden';
1625
		}
1626
1627
		// Setup Questions Query
1628
		$questions = array();
1629
		if ( 0 < $quiz_id ) {
1630
			$questions = $this->lesson_quiz_questions( $quiz_id );
1631
		}
1632
1633
		// Count questions
1634
		$question_count = 0;
1635 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...
1636
			if( $question->post_type == 'multiple_question' ) {
1637
				$question_number = get_post_meta( $question->ID, 'number', true );
1638
				$question_count += $question_number;
1639
			} else {
1640
				++$question_count;
1641
			}
1642
		}
1643
1644
		$settings = array(
1645
			array(
1646
				'id' 			=> 'pass_required',
1647
				'label'			=> __( 'Pass required to complete lesson', 'woothemes-sensei' ),
1648
				'description'	=> __( 'The passmark must be achieved before the lesson is complete.', 'woothemes-sensei' ),
1649
				'type'			=> 'checkbox',
1650
				'default'		=> '',
1651
				'checked'		=> 'on',
1652
			),
1653
			array(
1654
				'id' 			=> 'quiz_passmark',
1655
				'label'			=> __( 'Quiz passmark percentage', 'woothemes-sensei' ),
1656
				'description'	=> '',
1657
				'type'			=> 'number',
1658
				'default'		=> 0,
1659
				'placeholder'	=> 0,
1660
				'min'			=> 0,
1661
				'max'			=> 100,
1662
				'class'			=> $disable_passmark,
1663
			),
1664
			array(
1665
				'id' 			=> 'show_questions',
1666
				'label'			=> __( 'Number of questions to show', 'woothemes-sensei' ),
1667
				'description'	=> __( 'Show a random selection of questions from this quiz each time a student views it.', 'woothemes-sensei' ),
1668
				'type'			=> 'number',
1669
				'default'		=> '',
1670
				'placeholder'	=> __( 'All', 'woothemes-sensei' ),
1671
				'min'			=> 1,
1672
				'max'			=> $question_count,
1673
			),
1674
			array(
1675
				'id' 			=> 'random_question_order',
1676
				'label'			=> __( 'Randomise question order', 'woothemes-sensei' ),
1677
				'description'	=> '',
1678
				'type'			=> 'checkbox',
1679
				'default'		=> 'no',
1680
				'checked'		=> 'yes',
1681
			),
1682
			array(
1683
				'id' 			=> 'quiz_grade_type',
1684
				'label'			=> __( 'Grade quiz automatically', 'woothemes-sensei' ),
1685
				'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' ),
1686
				'type'			=> 'checkbox',
1687
				'default'		=> 'auto',
1688
				'checked'		=> 'auto',
1689
			),
1690
			array(
1691
				'id' 			=> 'enable_quiz_reset',
1692
				'label'			=> __( 'Allow user to retake the quiz', 'woothemes-sensei' ),
1693
				'description'	=> __( 'Enables the quiz reset button.', 'woothemes-sensei' ),
1694
				'type'			=> 'checkbox',
1695
				'default'		=> '',
1696
				'checked'		=> 'on',
1697
			),
1698
		);
1699
1700
		return apply_filters( 'sensei_quiz_settings', $settings );
1701
	}
1702
1703
	/**
1704
	 * enqueue_scripts function.
1705
	 *
1706
	 * @access public
1707
	 * @return void
1708
	 */
1709
	public function enqueue_scripts( $hook ) {
1710
		global  $post_type;
1711
1712
		$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question' ) );
1713
		$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
1714
		$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', 'sensei_analysis', 'sensei_learners', 'sensei_updates', 'woothemes-sensei-settings', 'lesson-order' ) );
1715
1716
		// Test for Write Panel Pages
1717
		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 ) ) ) {
1718
1719
			$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
1720
1721
			// Load the lessons script
1722
            wp_enqueue_media();
1723
			wp_enqueue_script( 'sensei-lesson-metadata', Sensei()->plugin_url . 'assets/js/lesson-metadata' . $suffix . '.js', array( 'jquery', 'select2' ,'jquery-ui-sortable' ), Sensei()->version, true );
1724
			wp_enqueue_script( 'sensei-lesson-chosen', Sensei()->plugin_url . 'assets/chosen/chosen.jquery' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
1725
			wp_enqueue_script( 'sensei-chosen-ajax', Sensei()->plugin_url . 'assets/chosen/ajax-chosen.jquery' . $suffix . '.js', array( 'jquery', 'sensei-lesson-chosen' ), Sensei()->version, true );
1726
1727
            // Load the bulk edit screen script
1728
            if( 'edit.php' == $hook && 'lesson'==$_GET['post_type'] ) {
1729
                wp_enqueue_script( 'sensei-lessons-bulk-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-bulk-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version , true);
1730
            }
1731
1732
			// Localise script
1733
			$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' ) );
1734
			$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' ) );
1735
			$data = array_merge( $translation_strings, $ajax_vars );
1736
			wp_localize_script( 'sensei-lesson-metadata', 'woo_localized_data', $data );
1737
1738
			// Chosen RTL
1739
			if ( is_rtl() ) {
1740
				wp_enqueue_script( 'sensei-chosen-rtl', Sensei()->plugin_url . 'assets/chosen/chosen-rtl' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
1741
			}
1742
1743
		}
1744
1745
	} // End enqueue_scripts()
1746
1747
	/**
1748
	 * Load in CSS styles where necessary.
1749
	 *
1750
	 * @access public
1751
	 * @since  1.4.0
1752
	 * @return void
1753
	 */
1754
	public function enqueue_styles ( $hook ) {
1755
		global  $post_type;
1756
1757
		$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question', 'sensei_message' ) );
1758
		$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
1759
		$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', 'sensei_analysis', 'sensei_learners', 'sensei_updates', 'woothemes-sensei-settings' ) );
1760
1761
		// Test for Write Panel Pages
1762 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...
1763
			wp_enqueue_style( 'woothemes-sensei-settings-api', esc_url( Sensei()->plugin_url . 'assets/css/settings.css' ), '', Sensei()->version );
1764
		}
1765
1766
	} // End enqueue_styles()
1767
1768
	/**
1769
	 * Add column headings to the "lesson" post list screen.
1770
	 * @access public
1771
	 * @since  1.0.0
1772
	 * @param  array $defaults
1773
	 * @return array $new_columns
1774
	 */
1775 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...
1776
		$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...
1777
		$new_columns['title'] = _x( 'Lesson Title', 'column name', 'woothemes-sensei' );
1778
		$new_columns['lesson-course'] = _x( 'Course', 'column name', 'woothemes-sensei' );
1779
		$new_columns['lesson-prerequisite'] = _x( 'Pre-requisite Lesson', 'column name', 'woothemes-sensei' );
1780
		if ( isset( $defaults['date'] ) ) {
1781
			$new_columns['date'] = $defaults['date'];
1782
		}
1783
		return $new_columns;
1784
	} // End add_column_headings()
1785
1786
	/**
1787
	 * Add data for our newly-added custom columns.
1788
	 * @access public
1789
	 * @since  1.0.0
1790
	 * @param  string $column_name
1791
	 * @param  int $id
1792
	 * @return void
1793
	 */
1794
	public function add_column_data ( $column_name, $id ) {
1795
		global $wpdb, $post;
1796
1797
		switch ( $column_name ) {
1798
			case 'id':
1799
				echo $id;
1800
			break;
1801 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...
1802
				$lesson_course_id = get_post_meta( $id, '_lesson_course', true);
1803
				if ( 0 < absint( $lesson_course_id ) ) {
1804
					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>';
1805
				} // End If Statement
1806
			break;
1807 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...
1808
				$lesson_prerequisite_id = get_post_meta( $id, '_lesson_prerequisite', true);
1809
				if ( 0 < absint( $lesson_prerequisite_id ) ) {
1810
					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>';
1811
				} // End If Statement
1812
			break;
1813
			default:
1814
			break;
1815
		} // End Switch Statement
1816
	} // End add_column_data()
1817
1818
	/**
1819
	 * lesson_add_course function.
1820
	 *
1821
	 * @access public
1822
	 * @return void
1823
	 */
1824
	public function lesson_add_course () {
1825
		global $current_user;
1826
		//Add nonce security to the request
1827
		if ( isset($_POST['lesson_add_course_nonce']) ) {
1828
			$nonce = esc_html( $_POST['lesson_add_course_nonce'] );
1829
		} // End If Statement
1830
		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...
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...
Bug introduced by
Are you sure the assignment to $updated is correct as $this->lesson_save_course($course_data) (which targets Sensei_Lesson::lesson_save_course()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

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
			die('');
1860
		} // End If Statement
1861
		// Parse POST data
1862
		// WP slashes all incoming data regardless of Magic Quotes setting (see wp_magic_quotes()), which means that
1863
		// even the $_POST['data'] encoded with encodeURIComponent has it's apostrophes slashed.
1864
		// So first restore the original unslashed apostrophes by removing those slashes
1865
		$data = wp_unslash( $_POST['data'] );
1866
		// Then parse the string to an array (note that parse_str automatically urldecodes all the variables)
1867
		$question_data = array();
1868
		parse_str($data, $question_data);
1869
		// Finally re-slash all elements to ensure consistancy for lesson_save_question()
1870
		$question_data = wp_slash( $question_data );
1871
		// Save the question
1872
		$return = false;
1873
		// Question Save and Delete logic
1874
		if ( isset( $question_data['action'] ) && ( $question_data['action'] == 'delete' ) ) {
1875
			// Delete the Question
1876
			$return = $this->lesson_delete_question($question_data);
1877
		} else {
1878
			// Save the Question
1879
			if ( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1880
				$current_user = wp_get_current_user();
1881
				$question_data['post_author'] = $current_user->ID;
1882
				$question_id = $this->lesson_save_question( $question_data );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $question_id is correct as $this->lesson_save_question($question_data) (which targets Sensei_Lesson::lesson_save_question()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1883
				$question_type = Sensei()->question->get_question_type( $question_id );
1884
1885
				$question_count = intval( $question_data['question_count'] );
1886
				++$question_count;
1887
1888
				$return = $this->quiz_panel_question( $question_type, $question_count, $question_id );
1889
			} // End If Statement
1890
		} // End If Statement
1891
1892
		echo $return;
1893
1894
		die();
1895
	} // End lesson_update_question()
1896
1897
	public function lesson_add_multiple_questions() {
1898
1899
		$return = '';
1900
1901
		//Add nonce security to the request
1902
		$nonce = '';
1903
		if( isset( $_POST['lesson_add_multiple_questions_nonce'] ) ) {
1904
			$nonce = esc_html( $_POST['lesson_add_multiple_questions_nonce'] );
1905
		} // End If Statement
1906
1907
		if( ! wp_verify_nonce( $nonce, 'lesson_add_multiple_questions_nonce' ) ) {
1908
			die( $return );
1909
		} // End If Statement
1910
1911
		// Parse POST data
1912
		$data = $_POST['data'];
1913
		$question_data = array();
1914
		parse_str( $data, $question_data );
1915
1916
		if( is_array( $question_data ) ) {
1917
			if( isset( $question_data['quiz_id'] ) && ( 0 < absint( $question_data['quiz_id'] ) ) ) {
1918
1919
				$quiz_id = intval( $question_data['quiz_id'] );
1920
				$question_number = intval( $question_data['question_number'] );
1921
				$question_category = intval( $question_data['question_category'] );
1922
1923
				$question_counter = intval( $question_data['question_count'] );
1924
				++$question_counter;
1925
1926
				$cat = get_term( $question_category, 'question-category' );
1927
1928
				$post_data = array(
1929
					'post_content' => '',
1930
					'post_status' => 'publish',
1931
					'post_title' => sprintf( __( '%1$s Question(s) from %2$s', 'woothemes-sensei' ), $question_number, $cat->name ),
1932
					'post_type' => 'multiple_question'
1933
				);
1934
1935
				$multiple_id = wp_insert_post( $post_data );
1936
1937
				if( $multiple_id && ! is_wp_error( $multiple_id ) ) {
1938
					add_post_meta( $multiple_id, 'category', $question_category );
1939
					add_post_meta( $multiple_id, 'number', $question_number );
1940
					add_post_meta( $multiple_id, '_quiz_id', $quiz_id, false );
1941
					add_post_meta( $multiple_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_counter );
1942
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
1943
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
1944
					$return = $this->quiz_panel_question( 'category', $question_counter, $multiple_id, 'quiz', array( $cat->name, $question_number ) );
1945
				}
1946
			}
1947
		}
1948
1949
		echo $return;
1950
1951
		die();
1952
	}
1953
1954
	public function lesson_remove_multiple_questions() {
1955
1956
		//Add nonce security to the request
1957
		$nonce = '';
1958
		if( isset( $_POST['lesson_remove_multiple_questions_nonce'] ) ) {
1959
			$nonce = esc_html( $_POST['lesson_remove_multiple_questions_nonce'] );
1960
		} // End If Statement
1961
1962
		if( ! wp_verify_nonce( $nonce, 'lesson_remove_multiple_questions_nonce' ) ) {
1963
			die('');
1964
		} // End If Statement
1965
1966
		// Parse POST data
1967
		$data = $_POST['data'];
1968
		$question_data = array();
1969
		parse_str( $data, $question_data );
1970
1971
		if( is_array( $question_data ) ) {
1972
			wp_delete_post( $question_data['question_id'], true );
1973
		}
1974
1975
		die( 'Deleted' );
1976
	}
1977
1978
	public function get_question_category_limit() {
1979
1980
		// Set default
1981
		$return = 1;
1982
1983
		// Parse POST data
1984
		$data = $_POST['data'];
1985
		$cat_data = array();
1986
		parse_str( $data, $cat_data );
1987
1988
		if( isset( $cat_data['cat'] ) && '' != $cat_data['cat'] ) {
1989
			$cat = get_term( $cat_data['cat'], 'question-category' );
1990
			if( isset( $cat->count ) ) {
1991
				$return = $cat->count;
1992
			}
1993
		}
1994
1995
		echo $return;
1996
1997
		die('');
1998
	}
1999
2000
	public function lesson_add_existing_questions() {
2001
2002
		//Add nonce security to the request
2003
		$nonce = '';
2004
		if( isset( $_POST['lesson_add_existing_questions_nonce'] ) ) {
2005
			$nonce = esc_html( $_POST['lesson_add_existing_questions_nonce'] );
2006
		} // End If Statement
2007
2008
		if( ! wp_verify_nonce( $nonce, 'lesson_add_existing_questions_nonce' ) ) {
2009
			die('');
2010
		} // End If Statement
2011
2012
		// Parse POST data
2013
		$data = $_POST['data'];
2014
		$question_data = array();
2015
		parse_str( $data, $question_data );
2016
2017
		$return = '';
2018
2019
		if( is_array( $question_data ) ) {
2020
2021
			if( isset( $question_data['questions'] ) && '' != $question_data['questions'] ) {
2022
2023
				$questions = explode( ',', trim( $question_data['questions'], ',' ) );
2024
				$quiz_id = $question_data['quiz_id'];
2025
				$question_count = intval( $question_data['question_count'] );
2026
2027
				foreach( $questions as $question_id ) {
2028
2029
					++$question_count;
2030
2031
					$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2032 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...
2033
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2034
						$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2035
						update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2036
			    	}
2037
2038
			    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2039
					$question_type = Sensei()->question->get_question_type( $question_id );
2040
2041
					$return .= $this->quiz_panel_question( $question_type, $question_count, $question_id );
2042
				}
2043
			}
2044
		}
2045
2046
		echo $return;
2047
2048
		die('');
2049
	}
2050
2051 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...
2052
		//Add nonce security to the request
2053
		if ( isset($_POST['lesson_update_grade_type_nonce']) ) {
2054
			$nonce = esc_html( $_POST['lesson_update_grade_type_nonce'] );
2055
		} // End If Statement
2056
		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...
2057
			die('');
2058
		} // End If Statement
2059
		// Parse POST data
2060
		$data = $_POST['data'];
2061
		$quiz_data = array();
2062
		parse_str($data, $quiz_data);
2063
		update_post_meta( $quiz_data['quiz_id'], '_quiz_grade_type', $quiz_data['quiz_grade_type'] );
2064
		die();
2065
	}
2066
2067
	public function lesson_update_question_order() {
2068
		// Add nonce security to the request
2069
		if ( isset($_POST['lesson_update_question_order_nonce']) ) {
2070
			$nonce = esc_html( $_POST['lesson_update_question_order_nonce'] );
2071
		} // End If Statement
2072
		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...
2073
			die('');
2074
		} // End If Statement
2075
		// Parse POST data
2076
		$data = $_POST['data'];
2077
		$quiz_data = array();
2078
		parse_str($data, $quiz_data);
2079
		if( strlen( $quiz_data['question_order'] ) > 0 ) {
2080
			$questions = explode( ',', $quiz_data['question_order'] );
2081
			$o = 1;
2082
			foreach( $questions as $question_id ) {
2083
				update_post_meta( $question_id, '_quiz_question_order' . $quiz_data['quiz_id'], $quiz_data['quiz_id'] . '000' . $o );
2084
				++$o;
2085
			}
2086
			update_post_meta( $quiz_data['quiz_id'], '_question_order', $questions );
2087
		}
2088
		die();
2089
	}
2090
2091 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...
2092
		//Add nonce security to the request
2093
		if ( isset($_POST['lesson_update_question_order_random_nonce']) ) {
2094
			$nonce = esc_html( $_POST['lesson_update_question_order_random_nonce'] );
2095
		} // End If Statement
2096
		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...
2097
			die('');
2098
		} // End If Statement
2099
		// Parse POST data
2100
		$data = $_POST['data'];
2101
		$quiz_data = array();
2102
		parse_str($data, $quiz_data);
2103
		update_post_meta( $quiz_data['quiz_id'], '_random_question_order', $quiz_data['random_question_order'] );
2104
		die();
2105
	}
2106
2107
	/**
2108
	 * lesson_save_course function.
2109
	 *
2110
	 * @access private
2111
	 * @param array $data (default: array())
2112
	 * @return void
2113
	 */
2114
	private function lesson_save_course( $data = array() ) {
2115
		global $current_user;
2116
		$return = false;
2117
		// Setup the course data
2118
		$course_id = 0;
2119
		$course_content = '';
2120
		$course_title = '';
2121
		$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...
2122
		$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...
2123 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...
2124
			$course_id = absint( $data[ 'course_id' ] );
2125
		} // End If Statement
2126
		if ( isset( $data[ 'course_title' ] ) && ( '' != $data[ 'course_title' ] ) ) {
2127
			$course_title = $data[ 'course_title' ];
2128
		} // End If Statement
2129
		$post_title = $course_title;
2130
		if ( isset($data[ 'post_author' ]) ) {
2131
			$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...
2132
		} else {
2133
			$current_user = wp_get_current_user();
2134
			$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...
2135
		} // End If Statement
2136
		$post_status = 'publish';
2137
		$post_type = 'course';
2138
		if ( isset( $data[ 'course_content' ] ) && ( '' != $data[ 'course_content' ] ) ) {
2139
			$course_content = $data[ 'course_content' ];
2140
		} // End If Statement
2141
		$post_content = $course_content;
2142
		// Course Query Arguments
2143
		$post_type_args = array(	'post_content' => $post_content,
2144
  		    						'post_status' => $post_status,
2145
  		    						'post_title' => $post_title,
2146
  		    						'post_type' => $post_type
2147
  		    						);
2148
  		// Only save if there is a valid title
2149
  		if ( $post_title != '' ) {
2150
  		    // Check for prerequisite courses & product id
2151
  		    $course_prerequisite_id = absint( $data[ 'course_prerequisite' ] );
2152
  		    $course_woocommerce_product_id = absint( $data[ 'course_woocommerce_product' ] );
2153
  		    $course_category_id = absint( $data[ 'course_category' ] );
2154
  		    if ( 0 == $course_woocommerce_product_id ) { $course_woocommerce_product_id = '-'; }
2155
  		    // Insert or Update the Lesson Quiz
2156
		    if ( 0 < $course_id ) {
2157
		    	$post_type_args[ 'ID' ] = $course_id;
2158
		    	$course_id = wp_update_post($post_type_args);
2159
		    	update_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2160
		    	update_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2161
		    	if ( 0 < $course_category_id ) {
2162
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2163
		    	} // End If Statement
2164
		    } else {
2165
		    	$course_id = wp_insert_post($post_type_args);
2166
		    	add_post_meta( $course_id, '_course_prerequisite', $course_prerequisite_id );
2167
		    	add_post_meta( $course_id, '_course_woocommerce_product', $course_woocommerce_product_id );
2168
		    	if ( 0 < $course_category_id ) {
2169
		    		wp_set_object_terms( $course_id, $course_category_id, 'course-category' );
2170
		    	} // End If Statement
2171
		    } // End If Statement
2172
		} // End If Statement
2173
  		// Check that the insert or update saved by testing the post id
2174
  		if ( 0 < $course_id ) {
2175
  			$return = $course_id;
2176
  		} // End If Statement
2177
  		return $return;
2178
  	} // End lesson_save_course()
2179
2180
2181
	/**
2182
	 * lesson_save_question function.
2183
	 *
2184
	 * @access private
2185
	 * @param array $data (default: array())
2186
	 * @return void
2187
	 */
2188
	public function lesson_save_question( $data = array(), $context = 'quiz' ) {
2189
		$return = false;
2190
		// Save the Questions
2191
		// Setup the Question data
2192
		$question_id = 0;
2193
		$question_text = '';
2194
		$question_right_answer = '';
2195
		$question_wrong_answers = $question_right_answers = array();
2196
		$question_type = 'multiple-choice';
2197
		$question_category = '';
2198
2199
		// Handle Question Type
2200
		if ( isset( $data[ 'question_type' ] ) && ( '' != $data[ 'question_type' ] ) ) {
2201
			$question_type = $data[ 'question_type' ];
2202
		} // End If Statement
2203
2204
		if ( isset( $data[ 'question_category' ] ) && ( '' != $data[ 'question_category' ] ) ) {
2205
			$question_category = $data[ 'question_category' ];
2206
		} // End If Statement
2207
2208 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...
2209
			$question_id = absint( $data[ 'question_id' ] );
2210
		} // End If Statement
2211
		if ( isset( $data[ 'question' ] ) && ( '' != $data[ 'question' ] ) ) {
2212
			$question_text = $data[ 'question' ];
2213
		} // End If Statement
2214
		$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...
2215
		// Handle Default Fields (multiple choice)
2216
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answers' ] ) && ( '' != $data[ 'question_right_answers' ] ) ) {
2217
			$question_right_answers = $data[ 'question_right_answers' ];
2218
		} // End If Statement
2219
		elseif ( 'multiple-choice' == $question_type && isset( $data[ 'question_right_answer' ] ) && ( '' != $data[ 'question_right_answer' ] ) ) {
2220
			$question_right_answer = $data[ 'question_right_answer' ];
2221
		} // End If Statement
2222
		if ( 'multiple-choice' == $question_type && isset( $data[ 'question_wrong_answers' ] ) && ( '' != $data[ 'question_wrong_answers' ] ) ) {
2223
			$question_wrong_answers = $data[ 'question_wrong_answers' ];
2224
		} // End If Statement
2225
		// Handle Boolean Fields - Edit
2226
		if ( 'boolean' == $question_type && isset( $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) && ( '' != $data[ 'question_' . $question_id . '_right_answer_boolean' ] ) ) {
2227
			$question_right_answer = $data[ 'question_' . $question_id . '_right_answer_boolean' ];
2228
		} // End If Statement
2229
		// Handle Boolean Fields - Add
2230
		if ( 'boolean' == $question_type && isset( $data[ 'question_right_answer_boolean' ] ) && ( '' != $data[ 'question_right_answer_boolean' ] ) ) {
2231
			$question_right_answer = $data[ 'question_right_answer_boolean' ];
2232
		} // End If Statement
2233
		// Handle Gap Fill Fields
2234
		if ( 'gap-fill' == $question_type && isset( $data[ 'add_question_right_answer_gapfill_gap' ] ) && '' != $data[ 'add_question_right_answer_gapfill_gap' ] ) {
2235
			$question_right_answer = $data[ 'add_question_right_answer_gapfill_pre' ] . '||' . $data[ 'add_question_right_answer_gapfill_gap' ] . '||' . $data[ 'add_question_right_answer_gapfill_post' ];
2236
		} // End If Statement
2237
		// Handle Multi Line Fields
2238
		if ( 'multi-line' == $question_type && isset( $data[ 'add_question_right_answer_multiline' ] ) && ( '' != $data[ 'add_question_right_answer_multiline' ] ) ) {
2239
			$question_right_answer = $data[ 'add_question_right_answer_multiline' ];
2240
		} // End If Statement
2241
		// Handle Single Line Fields
2242
		if ( 'single-line' == $question_type && isset( $data[ 'add_question_right_answer_singleline' ] ) && ( '' != $data[ 'add_question_right_answer_singleline' ] ) ) {
2243
			$question_right_answer = $data[ 'add_question_right_answer_singleline' ];
2244
		} // End If Statement
2245
		// Handle File Upload Fields
2246
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_right_answer_fileupload' ] ) && ( '' != $data[ 'add_question_right_answer_fileupload' ] ) ) {
2247
			$question_right_answer = $data[ 'add_question_right_answer_fileupload' ];
2248
		} // End If Statement
2249
		if ( 'file-upload' == $question_type && isset( $data[ 'add_question_wrong_answer_fileupload' ] ) && ( '' != $data[ 'add_question_wrong_answer_fileupload' ] ) ) {
2250
			$question_wrong_answers = array( $data[ 'add_question_wrong_answer_fileupload' ] );
2251
		} // End If Statement
2252
2253
		// Handle Question Grade
2254
		if ( isset( $data[ 'question_grade' ] ) && ( '' != $data[ 'question_grade' ] ) ) {
2255
			$question_grade = $data[ 'question_grade' ];
2256
		} // End If Statement
2257
2258
		// Handle Answer Feedback
2259
		$answer_feedback = '';
2260
		if ( isset( $data[ 'answer_feedback_boolean' ] ) && !empty( $data[ 'answer_feedback_boolean' ] ) ) {
2261
2262
            $answer_feedback = $data[ 'answer_feedback_boolean' ];
2263
2264
		}elseif( isset( $data[ 'answer_feedback_multiple_choice' ] ) && !empty( $data[ 'answer_feedback_multiple_choice' ] ) ){
2265
2266
            $answer_feedback = $data[ 'answer_feedback_multiple_choice' ];
2267
2268
        }elseif( isset( $data[ 'answer_feedback' ] )  ){
2269
2270
            $answer_feedback = $data[ 'answer_feedback' ];
2271
2272
        } // End If Statement
2273
2274
		$post_title = $question_text;
2275
		$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...
2276
		$post_status = 'publish';
2277
		$post_type = 'question';
2278
		// Handle the extended question text
2279
		if ( isset( $data[ 'question_description' ] ) && ( '' != $data[ 'question_description' ] ) ) {
2280
			$post_content = $data[ 'question_description' ];
2281
		}
2282
		else {
2283
			$post_content = '';
2284
		}
2285
		// Question Query Arguments
2286
		$post_type_args = array(	'post_content' => $post_content,
2287
  		    						'post_status' => $post_status,
2288
  		    						'post_title' => $post_title,
2289
  		    						'post_type' => $post_type
2290
  		    						);
2291
2292
  		// Remove empty values and reindex the array
2293
  		if ( is_array( $question_right_answers ) && 0 < count($question_right_answers) ) {
2294
  			$question_right_answers_array = array_values( array_filter( $question_right_answers, 'strlen' ) );
2295
  			$question_right_answers = array();
2296
2297
  			foreach( $question_right_answers_array as $answer ) {
2298
  				if( ! in_array( $answer, $question_right_answers ) ) {
2299
  					$question_right_answers[] = $answer;
2300
  				}
2301
  			}
2302
  			if ( 0 < count($question_right_answers) ) {
2303
  				$question_right_answer = $question_right_answers;
2304
  			}
2305
  		} // End If Statement
2306
  		$right_answer_count = count( $question_right_answer );
2307
2308
		// Remove empty values and reindex the array
2309
  		if ( is_array( $question_wrong_answers ) ) {
2310
  			$question_wrong_answers_array = array_values( array_filter( $question_wrong_answers, 'strlen' ) );
2311
  			$question_wrong_answers = array();
2312
  		} // End If Statement
2313
2314
  		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...
2315
  			if( ! in_array( $answer, $question_wrong_answers ) ) {
2316
  				$question_wrong_answers[] = $answer;
2317
  			}
2318
  		}
2319
2320
  		$wrong_answer_count = count( $question_wrong_answers );
2321
2322
  		// Only save if there is a valid title
2323
  		if ( $post_title != '' ) {
2324
2325
  			// Get Quiz ID for the question
2326
  		    $quiz_id = $data['quiz_id'];
2327
2328
  		    // Get question media
2329
			$question_media = $data['question_media'];
2330
2331
  		    // Get answer order
2332
  		    $answer_order = '';
2333
  		    if( isset( $data['answer_order'] ) ) {
2334
				$answer_order = $data['answer_order'];
2335
			}
2336
2337
			// Get random order selection
2338
			$random_order = 'no';
2339
			if( isset( $data['random_order'] ) ) {
2340
				$random_order = $data['random_order'];
2341
			}
2342
2343
  		    // Insert or Update the question
2344
  		    if ( 0 < $question_id ) {
2345
2346
  		    	$post_type_args[ 'ID' ] = $question_id;
2347
		    	$question_id = wp_update_post( $post_type_args );
2348
2349
		    	// Update poast meta
2350
		    	if( 'quiz' == $context ) {
2351
		    		$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2352
		    		if( ! in_array( $quiz_id, $quizzes ) ) {
2353
			    		add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2354
			    	}
2355
		    	}
2356
2357
		    	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...
2358
		    	update_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2359
		    	update_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2360
		    	update_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2361
		    	update_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2362
		    	update_post_meta( $question_id, '_question_media', $question_media );
2363
		    	update_post_meta( $question_id, '_answer_order', $answer_order );
2364
		    	update_post_meta( $question_id, '_random_order', $random_order );
2365
2366
		    	if( 'quiz' != $context ) {
2367
		    		wp_set_post_terms( $question_id, array( $question_type ), 'question-type', false );
2368
		    	}
2369
				// Don't store empty value, no point
2370
				if ( !empty($answer_feedback) ) {
2371
					update_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2372
				}
2373
2374
		    } else {
2375
				$question_id = wp_insert_post( $post_type_args );
2376
				$question_count = intval( $data['question_count'] );
2377
				++$question_count;
2378
2379
				// Set post meta
2380 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...
2381
					add_post_meta( $question_id, '_quiz_id', $quiz_id, false );
2382
					$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
2383
					update_post_meta( $lesson_id, '_quiz_has_questions', '1' );
2384
				}
2385
2386
				if( isset( $question_grade ) ) {
2387
		    		add_post_meta( $question_id, '_question_grade', $question_grade );
2388
		    	}
2389
		    	add_post_meta( $question_id, '_question_right_answer', $question_right_answer );
2390
		    	add_post_meta( $question_id, '_right_answer_count', $right_answer_count );
2391
		    	add_post_meta( $question_id, '_question_wrong_answers', $question_wrong_answers );
2392
		    	add_post_meta( $question_id, '_wrong_answer_count', $wrong_answer_count );
2393
		    	add_post_meta( $question_id, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $question_count );
2394
		    	add_post_meta( $question_id, '_question_media', $question_media );
2395
		    	add_post_meta( $question_id, '_answer_order', $answer_order );
2396
		    	add_post_meta( $question_id, '_random_order', $random_order );
2397
				// Don't store empty value, no point
2398
				if ( !empty($answer_feedback) ) {
2399
					add_post_meta( $question_id, '_answer_feedback', $answer_feedback );
2400
				}
2401
2402
		    	// Set the post terms for question-type
2403
			    wp_set_post_terms( $question_id, array( $question_type ), 'question-type' );
2404
2405
			    if( $question_category ) {
2406
	    			wp_set_post_terms( $question_id, array( $question_category ), 'question-category' );
2407
	    		}
2408
2409
		    } // End If Statement
2410
		} // End If Statement
2411
  		// Check that the insert or update saved by testing the post id
2412
  		if ( 0 < $question_id ) {
2413
  			$return = $question_id;
2414
  		} // End If Statement
2415
  		return $return;
2416
  	} // End lesson_question_save()
2417
2418
2419
	/**
2420
	 * lesson_delete_question function.
2421
	 *
2422
	 * @access private
2423
	 * @param array $data (default: array())
2424
	 * @return void
2425
	 */
2426
	private function lesson_delete_question( $data = array() ) {
2427
2428
		// Get which question to delete
2429
		$question_id = 0;
2430 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...
2431
			$question_id = absint( $data[ 'question_id' ] );
2432
		} // End If Statement
2433
		// Delete the question
2434
		if ( 0 < $question_id ) {
2435
			$quizzes = get_post_meta( $question_id, '_quiz_id', false );
2436
2437
			foreach( $quizzes as $quiz_id ) {
2438
				if( $quiz_id == $data['quiz_id'] ) {
2439
					delete_post_meta( $question_id, '_quiz_id', $quiz_id );
2440
				}
2441
			}
2442
2443
			return true;
2444
		} // End If Statement
2445
		return false;
2446
	} // End lesson_delete_question()
2447
2448
2449
	/**
2450
	 * lesson_complexities function.
2451
	 *
2452
	 * @access public
2453
	 * @return array $lesson_complexities
2454
	 */
2455
	public function lesson_complexities() {
2456
2457
		// V2 - make filter for this array
2458
        $lesson_complexities = array( 	'easy' => __( 'Easy', 'woothemes-sensei' ),
2459
									'std' => __( 'Standard', 'woothemes-sensei' ),
2460
									'hard' => __( 'Hard', 'woothemes-sensei' )
2461
									);
2462
2463
		return $lesson_complexities;
2464
2465
	} // End lesson_complexities
2466
2467
2468
	/**
2469
	 * lesson_count function.
2470
	 *
2471
	 * @access public
2472
	 * @param string $post_status (default: 'publish')
2473
	 * @return int
2474
	 */
2475
	public function lesson_count( $post_status = 'publish', $course_id = false ) {
2476
2477
		$post_args = array(	'post_type'         => 'lesson',
2478
							'posts_per_page'    => -1,
2479
//							'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...
2480
//							'order'             => 'ASC',
2481
							'post_status'       => $post_status,
2482
							'suppress_filters'  => 0,
2483
							'fields'            => 'ids',
2484
							);
2485
		if( $course_id ) {
2486
			$post_args['meta_query'][] = array(
2487
				'key' => '_lesson_course',
2488
				'value' => $course_id,
2489
			);
2490
		}
2491
		else {
2492
			// Simple check for connection to a Course
2493
			$post_args['meta_query'][] = array(
2494
				'key' => '_lesson_course',
2495
				'value' => 0,
2496
				'compare' => '>=',
2497
			);
2498
		}
2499
2500
		// Allow WP to generate the complex final query, just shortcut to only do an overall count
2501
//		add_filter( 'posts_clauses', array( 'WooThemes_Sensei_Utils', 'get_posts_count_only_filter' ) );
2502
		$lessons_query = new WP_Query( apply_filters( 'sensei_lesson_count', $post_args ) );
2503
//		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...
2504
2505
		return count( $lessons_query->posts );
2506
	} // End lesson_count()
2507
2508
2509
	/**
2510
	 * lesson_quizzes function.
2511
	 *
2512
	 * @access public
2513
	 * @param int $lesson_id (default: 0)
2514
	 * @param string $post_status (default: 'publish')
2515
	 * @param string $fields (default: 'ids')
2516
	 * @return int $quiz_id
2517
	 */
2518 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...
2519
2520
		$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...
2521
2522
		$post_args = array(	'post_type' 		=> 'quiz',
2523
							'posts_per_page' 		=> 1,
2524
							'orderby'         	=> 'title',
2525
    						'order'           	=> 'DESC',
2526
    						'post_parent'      	=> $lesson_id,
2527
    						'post_status'		=> $post_status,
2528
							'suppress_filters' 	=> 0,
2529
							'fields'            => $fields
2530
							);
2531
		$posts_array = get_posts( $post_args );
2532
        $quiz_id = array_shift($posts_array);
2533
2534
		return $quiz_id;
2535
	} // End lesson_quizzes()
2536
2537
2538
	/**
2539
	 * Fetches all the questions for a quiz depending on certain conditions.
2540
     *
2541
     * Determine which questions should be shown depending on:
2542
     * - admin/teacher selected questions to be shown
2543
     * - questions shown to a user previously (saved as asked questions)
2544
     * - limit number of questions lesson setting
2545
	 *
2546
     * @since 1.0
2547
	 * @param int $quiz_id (default: 0)
2548
	 * @param string $post_status (default: 'publish')
2549
	 * @param string $orderby (default: 'meta_value_num title')
2550
	 * @param string $order (default: 'ASC')
2551
     *
2552
	 * @return array $questions { $question type WP_Post }
2553
	 */
2554
	public function lesson_quiz_questions( $quiz_id = 0, $post_status = 'any', $orderby = 'meta_value_num title', $order = 'ASC' ) {
2555
2556
		$quiz_id = (string) $quiz_id;
2557
        $quiz_lesson_id = Sensei()->quiz->get_lesson_id( $quiz_id );
2558
2559
        // setup the user id
2560
        if( is_admin() ) {
2561
            $user_id = isset( $_GET['user'] ) ? $_GET['user'] : '' ;
2562
        } else {
2563
            $user_id = get_current_user_id();
2564
        }
2565
2566
        // get the users current status on the lesson
2567
        $user_lesson_status = Sensei_Utils::user_lesson_status( $quiz_lesson_id, $user_id );
2568
2569
		// Set the default question order if it has not already been set for this quiz
2570
		$this->set_default_question_order( $quiz_id );
2571
2572
		// If viewing quiz on the frontend then show questions in random order if set
2573
		if ( ! is_admin() ) {
2574
			$random_order = get_post_meta( $quiz_id, '_random_question_order', true );
2575
			if( $random_order && $random_order == 'yes' ) {
2576
				$orderby = 'rand';
2577
			}
2578
		}
2579
2580
		// Get all questions and multiple questions
2581
		$question_query_args = array(
2582
			'post_type' 		=> array( 'question', 'multiple_question' ),
2583
			'posts_per_page' 	=> -1,
2584
			'meta_key'        	=> '_quiz_question_order' . $quiz_id,
2585
			'orderby'         	=> $orderby,
2586
			'order'           	=> $order,
2587
			'meta_query'		=> array(
2588
				array(
2589
					'key'       => '_quiz_id',
2590
					'value'     => $quiz_id,
2591
				)
2592
			),
2593
			'post_status'		=> $post_status,
2594
			'suppress_filters' 	=> 0
2595
		);
2596
2597
        //query the questions
2598
		$questions_query = new WP_Query( $question_query_args );
2599
2600
        // Set return array to initially include all items
2601
        $questions = $questions_query->posts;
2602
2603
        // set the questions array that will be manipulated within this function
2604
        $questions_array = $questions_query->posts;
2605
2606
		// If viewing quiz on frontend or in grading then only single questions must be shown
2607
		$selected_questions = false;
2608
		if( ! is_admin() || ( is_admin() && isset( $_GET['page'] ) && 'sensei_grading' == $_GET['page'] && isset( $_GET['user'] ) && isset( $_GET['quiz_id'] ) ) ) {
2609
2610
			// Fetch the questions that the user was asked in their quiz if they have already completed it
2611
			$questions_asked_string = !empty( $user_lesson_status->comment_ID) ? get_comment_meta( $user_lesson_status->comment_ID, 'questions_asked', true ) : false;
2612
			if( !empty($questions_asked_string) ) {
2613
2614
				$selected_questions = explode( ',', $questions_asked_string );
2615
2616
				// Fetch each question in the order in which they were asked
2617
				$questions = array();
2618
				foreach( $selected_questions as $question_id ) {
2619
					if( ! $question_id ) continue;
2620
					$question = get_post( $question_id );
2621
					if( ! isset( $question ) || ! isset( $question->ID ) ) continue;
2622
					$questions[] = $question;
2623
				}
2624
2625
			} else {
2626
2627
				// Otherwise, make sure that we convert all multiple questions into single questions
2628
2629
				$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...
2630
				$existing_questions = array();
2631
2632
				// Set array of questions that already exist so we can prevent duplicates from appearing
2633
				foreach( $questions_array as $question ) {
2634
					if( 'question' != $question->post_type ) continue;
2635
					$existing_questions[] = $question->ID;
2636
				}
2637
2638
				// Include only single questions in the return array
2639
				$questions_loop = $questions_array;
2640
				$questions_array = array();
2641
				foreach( $questions_loop as $k => $question ) {
2642
2643
					// If this is a single question then include it
2644
					if( 'question' == $question->post_type ) {
2645
						$questions_array[] = $question;
2646
					} else {
2647
2648
						// If this is a multiple question then get the specified amount of questions from the specified category
2649
						$question_cat = intval( get_post_meta( $question->ID, 'category', true ) );
2650
						$question_number = intval( get_post_meta( $question->ID, 'number', true ) );
2651
2652
						$qargs = array(
2653
							'post_type' 		=> 'question',
2654
							'posts_per_page' 		=> $question_number,
2655
							'orderby'         	=> $orderby,
2656
							'tax_query'			=> array(
2657
								array(
2658
									'taxonomy'  => 'question-category',
2659
									'field'     => 'term_id',
2660
									'terms'		=> $question_cat
2661
								)
2662
							),
2663
							'post_status'		=> $post_status,
2664
							'suppress_filters' 	=> 0,
2665
							'post__not_in'		=> $existing_questions,
2666
						);
2667
						$cat_questions = get_posts( $qargs );
2668
2669
						// Merge results into return array
2670
						$questions_array = array_merge( $questions_array, $cat_questions );
2671
2672
						// Add selected questions to existing questions array to prevent duplicates from being added
2673
						foreach( $questions_array as $cat_question ) {
2674
							if( in_array( $cat_question->ID, $existing_questions ) ) continue;
2675
							$existing_questions[] = $cat_question->ID;
2676
						}
2677
					}
2678
				}
2679
2680
				// Set return data
2681
				$questions = $questions_array;
2682
			}
2683
		}
2684
2685
		// 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
2686
		if( ! $selected_questions ) {
2687
2688
			// Only limit questions like this on the frontend
2689
			if( ! is_admin() ) {
2690
2691
				// Get number of questions to show
2692
				$show_questions = intval( get_post_meta( $quiz_id, '_show_questions', true ) );
2693
				if( $show_questions ) {
2694
2695
					// Get random set of array keys from selected questions array
2696
					$selected_questions = array_rand( $questions_array, $show_questions );
2697
2698
					// Loop through all questions and pick the the ones to be shown based on the random key selection
2699
					$questions = array();
2700
					foreach( $questions_array as $k => $question ) {
2701
2702
						// Random keys will always be an array, unless only one question is to be shown
2703
						if( is_array( $selected_questions ) ) {
2704
							if( in_array( $k, $selected_questions ) ) {
2705
								$questions[] = $question;
2706
							}
2707
						} elseif( 1 == $show_questions ) {
2708
							if ( $selected_questions == $k ) {
2709
								$questions[] = $question;
2710
							}
2711
						}
2712
					}
2713
				}
2714
			}
2715
		}
2716
2717
        // Save the questions that will be asked for the current user
2718
        // this happens only once per user/quiz, unless the user resets the quiz
2719
        if( ! is_admin() ){
2720
2721
            if( $user_lesson_status ) {
2722
2723
                $questions_asked = get_comment_meta($user_lesson_status->comment_ID, 'questions_asked', true);
2724
                if ( empty($questions_asked) && $user_lesson_status) {
2725
2726
                    $questions_asked = array();
2727
                    foreach ($questions as $question) {
2728
2729
                        $questions_asked[] = $question->ID;
2730
2731
                    }
2732
2733
                    // save the questions asked id
2734
                    $questions_asked_csv = implode(',', $questions_asked);
2735
                    update_comment_meta($user_lesson_status->comment_ID, 'questions_asked', $questions_asked_csv);
2736
2737
                }
2738
            }
2739
        }
2740
2741
        /**
2742
         * Filter the questions returned by Sensei_Lesson::lessons_quiz_questions
2743
         *
2744
         * @hooked Sensei_Teacher::allow_teacher_access_to_questions
2745
         * @since 1.8.0
2746
         */
2747
		return apply_filters( 'sensei_lesson_quiz_questions', $questions,  $quiz_id  );
2748
2749
	} // End lesson_quiz_questions()
2750
2751
	/**
2752
	 * Set the default quiz order
2753
	 * @param integer $quiz_id ID of quiz
2754
	 */
2755
	public function set_default_question_order( $quiz_id = 0 ) {
2756
2757
		if( $quiz_id ) {
2758
2759
			$question_order = get_post_meta( $quiz_id, '_question_order', true );
2760
2761
			if( ! $question_order ) {
2762
2763
				$args = array(
2764
					'post_type' 		=> 'question',
2765
					'posts_per_page' 		=> -1,
2766
					'orderby'         	=> 'ID',
2767
					'order'           	=> 'ASC',
2768
					'meta_query'		=> array(
2769
						array(
2770
							'key'       => '_quiz_id',
2771
							'value'     => $quiz_id
2772
						)
2773
					),
2774
					'post_status'		=> 'any',
2775
					'suppress_filters' 	=> 0
2776
				);
2777
				$questions = get_posts( $args );
2778
2779
				$o = 1;
2780
				foreach( $questions as $question ) {
2781
					add_post_meta( $question->ID, '_quiz_question_order' . $quiz_id, $quiz_id . '000' . $o, true );
2782
					$o++;
2783
				}
2784
			}
2785
		}
2786
2787
	}
2788
2789
	/**
2790
	 * lesson_image function.
2791
	 *
2792
	 * Handles output of the lesson image
2793
	 *
2794
	 * @access public
2795
	 * @param int $lesson_id (default: 0)
2796
	 * @param string $width (default: '100')
2797
	 * @param string $height (default: '100')
2798
	 * @return string
2799
	 */
2800
	public function lesson_image( $lesson_id = 0, $width = '100', $height = '100', $widget = false ) {
2801
2802
		$html = '';
2803
2804
		// Get Width and Height settings
2805
		if ( ( $width == '100' ) && ( $height == '100' ) ) {
2806
2807
			if ( is_singular( 'lesson' ) ) {
2808
2809
				if ( ! $widget && ! Sensei()->settings->settings[ 'lesson_single_image_enable' ] ) {
2810
2811
					return '';
2812
2813
				} // End If Statement
2814
2815
				$image_thumb_size = 'lesson_single_image';
2816
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2817
				$width = $dimensions['width'];
2818
				$height = $dimensions['height'];
2819
				$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...
2820
2821
			} else {
2822
2823
				if ( ! $widget && ! Sensei()->settings->settings[ 'course_lesson_image_enable' ] ) {
2824
2825
					return '';
2826
				} // End If Statement
2827
2828
				$image_thumb_size = 'lesson_archive_image';
2829
				$dimensions = Sensei()->get_image_size( $image_thumb_size );
2830
				$width = $dimensions['width'];
2831
				$height = $dimensions['height'];
2832
				$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...
2833
2834
			} // End If Statement
2835
2836
		} // End If Statement
2837
2838
		$img_url = '';
2839
2840
		if ( has_post_thumbnail( $lesson_id ) ) {
2841
2842
   			// Get Featured Image
2843
   			$img_url = get_the_post_thumbnail( $lesson_id, array( $width, $height ), array( 'class' => 'woo-image thumbnail alignleft') );
2844
2845 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...
2846
2847
 			// Display Image Placeholder if none
2848
			if ( Sensei()->settings->settings[ 'placeholder_images_enable' ] ) {
2849
2850
                $img_url = apply_filters( 'sensei_lesson_placeholder_image_url', '<img src="http://placehold.it/' . $width . 'x' . $height . '" class="woo-image thumbnail alignleft" />' );
2851
2852
			} // End If Statement
2853
2854
		} // End If Statement
2855
2856
		$html .= '<a href="' . get_permalink( $lesson_id ) . '" title="' . esc_attr( get_post_field( 'post_title', $lesson_id ) ) . '">' . $img_url . '</a>';
2857
2858
		return $html;
2859
2860
	} // End lesson_image()
2861
2862
	/**
2863
	 * Returns the the lesson excerpt.
2864
	 *
2865
	 * @param WP_Post $lesson
2866
     * @param bool $add_p_tags should the excerpt be wrapped by calling wpautop()
2867
	 * @return string
2868
	 */
2869
	public static function lesson_excerpt( $lesson = null, $add_p_tags = true ) {
2870
		$html = '';
2871
		if ( is_a( $lesson, 'WP_Post' ) && 'lesson' == $lesson->post_type ) {
2872
2873
            $excerpt =  $lesson->post_excerpt;
2874
2875
            // if $add_p_tags true wrap with <p> else return the excerpt as is
2876
            $html =  $add_p_tags ? wpautop( $excerpt ) : $excerpt;
2877
2878
		}
2879
		return apply_filters( 'sensei_lesson_excerpt', $html );
2880
2881
	} // End lesson_excerpt()
2882
2883
    /**
2884
     * Returns the course for a given lesson
2885
     *
2886
     * @since 1.7.4
2887
     * @access public
2888
     *
2889
     * @param int $lesson_id
2890
     * @return int|bool $course_id or bool when nothing is found.
2891
     */
2892
     public function get_course_id( $lesson_id ){
2893
2894
         if( ! isset( $lesson_id ) || empty( $lesson_id )
2895
         ||  'lesson' != get_post_type( $lesson_id ) ){
2896
             return false;
2897
         }
2898
2899
         $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true);
2900
2901
         // make sure the course id is valid
2902
         if( empty( $lesson_course_id )
2903
             || is_array( $lesson_course_id )
2904
             || intval( $lesson_course_id ) < 1
2905
             || 'course' != get_post_type( $lesson_course_id ) ){
2906
2907
             return false;
2908
2909
         }
2910
2911
         return $lesson_course_id;
2912
2913
     }// en get_course_id
2914
2915
    /**
2916
     * Add the admin all lessons screen edit options.
2917
     *
2918
     * The fields in this function work for both quick and bulk edit. The ID attributes is used
2919
     * by bulk edit javascript in the front end to retrieve the new values set byt the user. Then
2920
     * name attribute is will be used by the quick edit and submitted via standard POST. This
2921
     * will use this classes save_post_meta function to save the new field data.
2922
     *
2923
     * @hooked quick_edit_custom_box
2924
     * @hooked bulk_edit_custom_box
2925
     *
2926
     * @since 1.8.0
2927
     *
2928
     * @param string $column_name
2929
     * @param string $post_type
2930
     * @return void
2931
     */
2932
    public function all_lessons_edit_fields( $column_name, $post_type ) {
2933
2934
        // only show these options ont he lesson post type edit screen
2935
        if( 'lesson' != $post_type || 'lesson-course' != $column_name ){
2936
            return;
2937
        }
2938
2939
        ?>
2940
        <fieldset class="sensei-edit-field-set inline-edit-lesson">
2941
            <div class="sensei-inline-edit-col column-<?php echo $column_name ?>">
2942
                    <?php
2943
                    echo '<h4>' . __('Lesson Information', 'woothemes-sensei') . '</h4>';
2944
                    // create a nonce field to be  used as a security measure when saving the data
2945
                    wp_nonce_field( 'bulk-edit-lessons', '_edit_lessons_nonce' );
2946
                    wp_nonce_field( 'sensei-save-post-meta','woo_' . $this->token . '_nonce'  );
2947
2948
                    // unchanged option - we need this in because
2949
                    // the default option in bulk edit should not be empty. If it is
2950
                    // the user will erase data they didn't want to touch.
2951
                    $no_change_text = '-- ' . __('No Change', 'woothemes-sensei') . ' --';
2952
2953
                    //
2954
                    //course selection
2955
                    //
2956
                    $courses =  WooThemes_Sensei_Course::get_all_courses();
2957
                    $course_options = array();
2958
                    if ( count( $courses ) > 0 ) {
2959
                        foreach ($courses as $course ){
2960
                            $course_options[ $course->ID ] = get_the_title( $course->ID );
2961
                        }
2962
                    }
2963
                    //pre-append the no change option
2964
                    $course_options['-1']=  $no_change_text;
2965
                    $course_attributes = array( 'name'=> 'lesson_course', 'id'=>'sensei-edit-lesson-course' , 'class'=>' ' );
2966
                    $course_field =  Sensei_Utils::generate_drop_down( '-1', $course_options, $course_attributes );
2967
                    echo $this->generate_all_lessons_edit_field( __('Lesson Course', 'woothemes-sensei'),   $course_field  );
2968
2969
                    //
2970
                    // lesson complexity selection
2971
                    //
2972
                    $lesson_complexities =  $this->lesson_complexities();
2973
                    //pre-append the no change option
2974
                    $lesson_complexities['-1']=  $no_change_text;
2975
                    $complexity_dropdown_attributes = array( 'name'=> 'lesson_complexity', 'id'=>'sensei-edit-lesson-complexity' , 'class'=>' ');
2976
                    $complexity_filed =  Sensei_Utils::generate_drop_down( '-1', $lesson_complexities, $complexity_dropdown_attributes );
2977
                    echo $this->generate_all_lessons_edit_field( __('Lesson Complexity', 'woothemes-sensei'),   $complexity_filed  );
2978
2979
                    ?>
2980
2981
                    <h4><?php _e('Quiz Settings', 'woothemes-sensei'); ?> </h4>
2982
2983
                    <?php
2984
2985
                    //
2986
                    // Lesson require pass to complete
2987
                    //
2988
                    $pass_required_options = array(
2989
                        '-1' => $no_change_text,
2990
                         '0' => __('No','woothemes'),
2991
                         '1' => __('Yes','woothemes'),
2992
                    );
2993
2994
                    $pass_required_select_attributes = array( 'name'=> 'pass_required',
2995
                                                                'id'=> 'sensei-edit-lesson-pass-required',
2996
                                                                'class'=>' '   );
2997
                    $require_pass_field =  Sensei_Utils::generate_drop_down( '-1', $pass_required_options, $pass_required_select_attributes, false );
2998
                    echo $this->generate_all_lessons_edit_field( __('Pass required', 'woothemes-sensei'),   $require_pass_field  );
2999
3000
                    //
3001
                    // Quiz pass percentage
3002
                    //
3003
                    $quiz_pass_percentage_field = '<input name="quiz_passmark" id="sensei-edit-quiz-pass-percentage" type="number" />';
3004
                    echo $this->generate_all_lessons_edit_field( __('Pass Percentage', 'woothemes-sensei'), $quiz_pass_percentage_field  );
3005
3006
                    //
3007
                    // Enable quiz reset button
3008
                    //
3009
                    $quiz_reset_select__options = array(
3010
                        '-1' => $no_change_text,
3011
                        '0' => __('No','woothemes'),
3012
                        '1' => __('Yes','woothemes'),
3013
                    );
3014
                    $quiz_reset_name_id = 'sensei-edit-enable-quiz-reset';
3015
                    $quiz_reset_select_attributes = array( 'name'=> 'enable_quiz_reset', 'id'=>$quiz_reset_name_id, 'class'=>' ' );
3016
                    $quiz_reset_field =  Sensei_Utils::generate_drop_down( '-1', $quiz_reset_select__options, $quiz_reset_select_attributes, false );
3017
                    echo $this->generate_all_lessons_edit_field( __('Enable quiz reset button', 'woothemes-sensei'), $quiz_reset_field  );
3018
3019
                    ?>
3020
            </div>
3021
        </fieldset>
3022
    <?php
3023
    }// all_lessons_edit_fields
3024
3025
    /**
3026
     * Create the html for the edit field
3027
     *
3028
     * Wraps the passed in field and title combination with the correct html.
3029
     *
3030
     * @since 1.8.0
3031
     *
3032
     * @param string $title that will stand to the left of the field.
3033
     * @param string $field type markup for the field that must be wrapped.
3034
     * @return string $field_html
3035
     */
3036
    public function generate_all_lessons_edit_field( $title  ,$field ){
3037
3038
        $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...
3039
        $html = '<div class="inline-edit-group" >';
3040
        $html .=  '<span class="title">'. $title .'</span> ';
3041
        $html .= '<span class="input-text-wrap">';
3042
        $html .= $field;
3043
        $html .= '</span>';
3044
        $html .= '</label></div>';
3045
3046
        return $html ;
3047
3048
    }//end generate_all_lessons_edit_field
3049
3050
    /**
3051
     * Respond to the ajax call from the bulk edit save function. This comes
3052
     * from the admin all lesson screen.
3053
     *
3054
     * @since 1.8.0
3055
     * @return void
3056
     */
3057
    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...
3058
3059
        // verify all the data before attempting to save
3060
        if( ! isset( $_POST['security'] ) || ! check_ajax_referer( 'bulk-edit-lessons', 'security' )
3061
            ||  empty( $_POST[ 'post_ids' ] )  || ! is_array( $_POST[ 'post_ids' ] ) ) {
3062
            die();
3063
        }
3064
3065
        // get our variables
3066
        $new_course = sanitize_text_field(  $_POST['sensei_edit_lesson_course'] );
3067
        $new_complexity = sanitize_text_field(  $_POST['sensei_edit_complexity'] );
3068
        $new_pass_required = sanitize_text_field(  $_POST['sensei_edit_pass_required'] );
3069
        $new_pass_percentage = sanitize_text_field(  $_POST['sensei_edit_pass_percentage'] );
3070
        $new_enable_quiz_reset = sanitize_text_field(  $_POST['sensei_edit_enable_quiz_reset'] );
3071
        // store the values for all selected posts
3072
        foreach( $_POST[ 'post_ids' ] as $lesson_id ) {
3073
3074
            // get the quiz id needed for the quiz meta
3075
            $quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
3076
3077
            // do not save the items if the value is -1 as this
3078
            // means it was not changed
3079
3080
            // update lesson course
3081
            if( -1 != $new_course ){
3082
                update_post_meta( $lesson_id, '_lesson_course', $new_course );
3083
            }
3084
            // update lesson complexity
3085
            if( -1 != $new_complexity ){
3086
                update_post_meta( $lesson_id, '_lesson_complexity', $new_complexity );
3087
            }
3088
3089
            // Quiz Related settings
3090
            if( isset( $quiz_id) && 0 < intval( $quiz_id ) ) {
3091
3092
                // update pass required
3093
                if (-1 != $new_pass_required) {
3094
3095
                    $checked = $new_pass_required  ? 'on' : '';
3096
                    update_post_meta($quiz_id, '_pass_required', $checked);
3097
                    unset( $checked );
3098
                }
3099
3100
                // update pass percentage
3101
                if( !empty( $new_pass_percentage) && is_numeric( $new_pass_percentage ) ){
3102
3103
                        update_post_meta($quiz_id, '_quiz_passmark', $new_pass_percentage);
3104
3105
                }
3106
3107
                //
3108
                // update enable quiz reset
3109
                //
3110
                if (-1 != $new_enable_quiz_reset ) {
3111
3112
                    $checked = $new_enable_quiz_reset ? 'on' : ''  ;
3113
                    update_post_meta($quiz_id, '_enable_quiz_reset', $checked);
3114
                    unset( $checked );
3115
3116
                }
3117
3118
3119
            } // end if quiz
3120
3121
        }// end for each
3122
3123
        die();
3124
3125
    } // end save_all_lessons_edit_fields
3126
3127
    /**
3128
     * Loading the quick edit fields defaults.
3129
     *
3130
     * This function will localise the default values along with the script that will
3131
     * add these values to the inputs.
3132
     *
3133
     * NOTE: this function runs for each row in the edit column
3134
     *
3135
     * @since 1.8.0
3136
     * @return void
3137
     */
3138
    public function set_quick_edit_admin_defaults( $column_name, $post_id ){
3139
3140
        if( 'lesson-course' != $column_name ){
3141
            return;
3142
        }
3143
        // load the script
3144
        $suffix = defined( 'SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
3145
        wp_enqueue_script( 'sensei-lesson-quick-edit', Sensei()->plugin_url . 'assets/js/admin/lesson-quick-edit' . $suffix . '.js', array( 'jquery' ), Sensei()->version, true );
3146
3147
        // setup the values for all meta fields
3148
        $data = array();
3149
        foreach( $this->meta_fields as $field ){
3150
3151
            $data[$field] =  get_post_meta( $post_id, '_'.$field, true );
3152
3153
        }
3154
        // add quiz meta fields
3155
        $quiz_id = Sensei()->lesson->lesson_quizzes( $post_id );
3156
        foreach( Sensei()->quiz->meta_fields as $field ){
3157
3158
            $data[$field] =  get_post_meta( $quiz_id, '_'.$field, true );
3159
3160
        }
3161
3162
        wp_localize_script( 'sensei-lesson-quick-edit', 'sensei_quick_edit_'.$post_id, $data );
3163
3164
    }// end quick edit admin defaults
3165
3166
    /**
3167
     * Filter the classes for lessons on the single course page.
3168
     *
3169
     * Adds the nesecary classes depending on the user data
3170
     *
3171
     * @since 1.9.0
3172
     * @param array $classes
3173
     * @return array $classes
3174
     */
3175
    public static function single_course_lessons_classes( $classes ){
3176
3177
        if(  is_singular('course') ){
3178
3179
            global $post;
3180
            $course_id = $post->ID;
3181
3182
            $lesson_classes = array( 'course', 'post' );
3183
            if ( is_user_logged_in() ) {
3184
3185
                // Check if Lesson is complete
3186
                $single_lesson_complete = Sensei_Utils::user_completed_lesson( get_the_ID(), get_current_user_id() );
3187
                if ( $single_lesson_complete ) {
3188
3189
                    $lesson_classes[] = 'lesson-completed';
3190
3191
                } // End If Statement
3192
3193
            } // End If Statement
3194
3195
            $is_user_taking_course = Sensei_Utils::user_started_course( $course_id, get_current_user_id() );
3196
            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...
3197
3198
                $lesson_classes[] = 'lesson-preview';
3199
3200
            }
3201
3202
            $classes = array_merge( $classes, $lesson_classes  );
3203
3204
        }
3205
3206
        return $classes;
3207
3208
    }// end single_course_lessons_classes
3209
3210
    /**
3211
     * Output the lesson meta for the given lesson
3212
     *
3213
     * @since 1.9.0
3214
     * @param $lesson_id
3215
     */
3216
    public static function the_lesson_meta( $lesson_id ){
3217
3218
        global $wp_query;
3219
        $loop_lesson_number = $wp_query->current_post + 1;
3220
3221
        $course_id = Sensei()->lesson->get_course_id( $lesson_id );
3222
        $single_lesson_complete = false;
3223
        $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 3221 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...
3224
3225
        // Get Lesson data
3226
        $complexity_array = Sensei()->lesson->lesson_complexities();
3227
3228
        $lesson_complexity = get_post_meta( $lesson_id, '_lesson_complexity', true );
3229
        if ( '' != $lesson_complexity ) {
3230
3231
            $lesson_complexity = $complexity_array[$lesson_complexity];
3232
3233
        }
3234
        $user_info = get_userdata( absint( get_post()->post_author ) );
3235
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id);
3236
        $preview_label = '';
3237
        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...
3238
3239
            $preview_label = Sensei()->frontend->sensei_lesson_preview_title_text( $lesson_id);
3240
            $preview_label = '<span class="preview-heading">' . $preview_label . '</span>';
3241
3242
        }
3243
3244
3245
        $count_markup= '';
3246
        /**
3247
         * Filter for if you want the $lesson_count to show next to the lesson.
3248
         *
3249
         * @since 1.0
3250
         * @param bool default false.
3251
         */
3252
        if( apply_filters( 'sensei_show_lesson_numbers', false ) ) {
3253
3254
            $count_markup =  '<span class="lesson-number">' . $loop_lesson_number. '</span>';
3255
3256
        }
3257
3258
        $heading_link_title = sprintf( __( 'Start %s', 'woothemes-sensei' ), get_the_title( $lesson_id ) );
3259
3260
        ?>
3261
        <header>
3262
            <h2>
3263
                <a href="<?php echo esc_url_raw( get_permalink( $lesson_id ) ) ?>"
3264
                   title="<?php esc_attr_e( $heading_link_title ) ?>" >
3265
                    <?php echo $count_markup. get_the_title( $lesson_id ) . $preview_label; ?>
3266
                </a>
3267
            </h2>
3268
3269
            <p class="lesson-meta">
3270
3271
                <?php
3272
3273
                $meta_html = '';
3274
                $user_lesson_status = Sensei_Utils::user_lesson_status( get_the_ID(), get_current_user_id() );
3275
3276
                $lesson_length = get_post_meta( $lesson_id, '_lesson_length', true );
3277 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...
3278
3279
                    $meta_html .= '<span class="lesson-length">' .  __( 'Length: ', 'woothemes-sensei' ) . $lesson_length . __( ' minutes', 'woothemes-sensei' ) . '</span>';
3280
3281
                }
3282
3283
                if ( Sensei()->settings->get( 'lesson_author' ) ) {
3284
3285
                    $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>';
3286
3287
                } // End If Statement
3288
                if ( '' != $lesson_complexity ) {
3289
3290
                    $meta_html .= '<span class="lesson-complexity">' .  __( 'Complexity: ', 'woothemes-sensei' ) . $lesson_complexity .'</span>';
3291
3292
                }
3293
3294 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...
3295
3296
                    $meta_html .= '<span class="lesson-status complete">' .__( 'Complete', 'woothemes-sensei' ) .'</span>';
3297
3298
                } elseif ( $user_lesson_status ) {
3299
3300
                    $meta_html .= '<span class="lesson-status in-progress">' . __( 'In Progress', 'woothemes-sensei' ) .'</span>';
3301
3302
                } // End If Statement
3303
3304
                echo $meta_html;
3305
3306
                ?>
3307
3308
            </p> <!-- lesson meta -->
3309
3310
        </header>
3311
3312
    <?php
3313
3314
    } // end the_lesson_meta
3315
3316
    /**
3317
     * Output the lessons thumbnail
3318
     *
3319
     * 1.9.0
3320
     *
3321
     * @param $lesson_id
3322
     */
3323
    public static function the_lesson_thumbnail( $lesson_id ){
3324
3325
        if( empty( $lesson_id ) ){
3326
3327
            $lesson_id = get_the_ID();
3328
3329
        }
3330
3331
        if( 'lesson' != get_post_type( $lesson_id ) ){
3332
            return;
3333
        }
3334
3335
        echo Sensei()->lesson->lesson_image( $lesson_id );
3336
    }
3337
3338
3339
    /**
3340
     * Alter the sensei lesson excerpt.
3341
     *
3342
     * @since 1.9.0
3343
     * @param string $excerpt
3344
     * @return string $excerpt
3345
     */
3346
    public static function alter_the_lesson_excerpt( $excerpt ) {
3347
3348
        if ('lesson' == get_post_type(get_the_ID())){
3349
3350
            // remove this hooks to avoid an infinite loop.
3351
            remove_filter( 'get_the_excerpt', array( 'WooThemes_Sensei_Lesson','alter_the_lesson_excerpt') );
3352
3353
            return WooThemes_Sensei_Lesson::lesson_excerpt( get_post( get_the_ID() ) );
3354
        }
3355
3356
        return $excerpt;
3357
3358
    }// end the_lesson_excerpt
3359
3360
    /**
3361
     * Returns the lesson prerequisite for the given lesson id.
3362
     *
3363
     * @since 1.9.0
3364
     *
3365
     * @param $current_lesson_id
3366
     * @return mixed | bool | int $prerequisite_lesson_id or false
3367
     */
3368
    public static function get_lesson_prerequisite_id( $current_lesson_id  ){
3369
3370
        $prerequisite_lesson_id = get_post_meta( $current_lesson_id , '_lesson_prerequisite', true );
3371
3372
        // set ti to false if not a valid prerequisite lesson id
3373
        if(  empty( $prerequisite_lesson_id )
3374
            || 'lesson' != get_post_type( $prerequisite_lesson_id )
3375
            || $prerequisite_lesson_id == $current_lesson_id  ) {
3376
3377
            $prerequisite_lesson_id = false;
3378
3379
        }
3380
3381
        return apply_filters( 'sensei_lesson_prerequisite', $prerequisite_lesson_id, $current_lesson_id );
3382
3383
    }
3384
3385
    /**
3386
     * This function requires that you pass in the lesson you would like to check for
3387
     * a pre-requisite and not the pre-requisite. It will check if the
3388
     * lesson has a pre-requiste and then check if it is completed.
3389
     *
3390
     * @since 1.9.0
3391
     *
3392
     * @param $lesson_id
3393
     * @param $user_id
3394
     * @return bool
3395
     */
3396
    public  static function is_prerequisite_complete( $lesson_id, $user_id  ){
3397
3398 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...
3399
        || 'lesson' != get_post_type( $lesson_id )
3400
        ||  ! is_a( get_user_by( 'id', $user_id ), 'WP_User' )){
3401
3402
            return false;
3403
3404
        }
3405
3406
        $pre_requisite_id = (string) self::get_lesson_prerequisite_id( $lesson_id );
3407
3408
        // not a valid pre-requisite so pre-requisite is completed
3409
        if( 'lesson' != get_post_type( $pre_requisite_id )
3410
            || ! is_numeric( $pre_requisite_id ) ){
3411
3412
            return true;
3413
3414
        }
3415
3416
        return  Sensei_Utils::user_completed_lesson( $pre_requisite_id, $user_id );
3417
3418
    }// end is_prerequisite_complete
3419
3420
    /**
3421
     * Show the user not taking course message if it is the case
3422
     *
3423
     * @since 1.9.0
3424
     */
3425
    public  static function user_not_taking_course_message(){
3426
3427
        $lesson_id = get_the_ID();
3428
3429
        if( 'lesson' != get_post_type( $lesson_id ) ){
3430
            return;
3431
        }
3432
3433
        $is_preview = Sensei_Utils::is_preview_lesson( $lesson_id );
3434
        $pre_requisite_complete = self::is_prerequisite_complete( $lesson_id , get_current_user_id() );
3435
        $lesson_course_id = get_post_meta( $lesson_id, '_lesson_course', true );
3436
        $user_taking_course = Sensei_Utils::user_started_course( $lesson_course_id, get_current_user_id() );
3437
3438
        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...
3439
            ?>
3440
3441
            <div class="sensei-message alert">
3442
                <?php echo Sensei()->permissions_message['message']; ?>
3443
            </div>
3444
3445
            <?php
3446
3447
        }// end if
3448
3449
    } // end user_not_taking_course_message
3450
3451
    /**
3452
     * Outputs the lessons course signup lingk
3453
     *
3454
     * This hook runs inside the single lesson page.
3455
     *
3456
     * @since 1.9.0
3457
     */
3458
    public static function course_signup_link( ){
3459
3460
        $course_id =  Sensei()->lesson->get_course_id( get_the_ID() );
3461
3462
        if ( empty( $course_id ) || 'course' != get_post_type( $course_id ) || sensei_all_access() ) {
3463
3464
            return;
3465
3466
        }
3467
        ?>
3468
3469
        <section class="course-signup lesson-meta">
3470
3471
            <?php
3472
            $wc_post_id = (int) get_post_meta( $course_id, '_course_woocommerce_product', true );
3473
3474
            if ( Sensei_WC::is_woocommerce_active() && ( 0 < $wc_post_id ) ) {
3475
3476
                global $current_user;
3477
                if( is_user_logged_in() ) {
3478
                    wp_get_current_user();
3479
3480
                    $course_purchased = Sensei_Utils::sensei_customer_bought_product( $current_user->user_email, $current_user->ID, $wc_post_id );
0 ignored issues
show
Deprecated Code introduced by
The method Sensei_Utils::sensei_customer_bought_product() has been deprecated with message: since 1.9.0 use Sensei_WC::has_customer_bought_product($user_id, $product_id)

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

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

Loading history...
3481
3482
                    if( $course_purchased ) {
3483
3484
                        $prereq_course_id = get_post_meta( $course_id, '_course_prerequisite',true );
3485
                        $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>';
3486
                        ?>
3487
                            <div class="sensei-message info">
3488
3489
                                <?php  echo sprintf( __( 'Please complete %1$s before starting the lesson.', 'woothemes-sensei' ), $course_link ); ?>
3490
3491
                            </div>
3492
3493 View Code Duplication
                    <?php } else { ?>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3494
3495
                        <div class="sensei-message info">
3496
3497
                            <?php
3498
                            $course_link = '<a href="' . esc_url( get_permalink( $course_id ) )
3499
                                            . '"title="' . __( 'Sign Up', 'woothemes-sensei' )
3500
                                            . '">' . __( 'course', 'woothemes-sensei' )
3501
                                            . '</a>';
3502
3503
                            echo  sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), $course_link );
3504
3505
                            ?>
3506
3507
                        </div>
3508
                    <?php } ?>
3509
3510 View Code Duplication
                <?php } else { ?>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3511
3512
                    <div class="sensei-message info"><?php echo sprintf( __( 'Please purchase the %1$s before starting the lesson.', 'woothemes-sensei' ), '<a href="' . esc_url( get_permalink( $course_id ) ) . '" title="' . __( 'Sign Up', 'woothemes-sensei' )  . '">' . __( 'course', 'woothemes-sensei' ) . '</a>' ); ?></div>
3513
3514
                <?php } ?>
3515
3516
            <?php } else { ?>
3517
3518
            <?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 3460 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...
3519
3520
                <div class="sensei-message info">
3521
                    <?php
3522
                    $course_link =  '<a href="'
3523
                                        . esc_url( get_permalink( $course_id ) )
3524
                                        . '" title="' . __( 'Sign Up', 'woothemes-sensei' )
3525
                                        . '">' . __( 'course', 'woothemes-sensei' )
3526
                                    . '</a>';
3527
3528
                    echo sprintf( __( 'Please sign up for the %1$s before starting the lesson.', 'woothemes-sensei' ),  $course_link );
3529
                    ?>
3530
                </div>
3531
3532
            <?php endif; ?>
3533
3534
            <?php } // End If Statement ?>
3535
3536
        </section>
3537
3538
        <?php
3539
    }// end course_signup_link
3540
3541
    /**
3542
     * Show a message telling the user to complete the previous message if they haven't done so yet
3543
     *
3544
     * @since 1.9.0
3545
     */
3546
    public  static function prerequisite_complete_message(){
3547
3548
        $lesson_prerequisite =  WooThemes_Sensei_Lesson::get_lesson_prerequisite_id( get_the_ID() );
3549
        $lesson_has_pre_requisite = $lesson_prerequisite > 0;
3550
        if ( ! WooThemes_Sensei_Lesson::is_prerequisite_complete(  get_the_ID(), get_current_user_id() ) && $lesson_has_pre_requisite ) {
3551
3552
            $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>';
3553
            echo sprintf( __( 'You must first complete %1$s before viewing this Lesson', 'woothemes-sensei' ), $prerequisite_lesson_link );
3554
3555
        }
3556
3557
    }
3558
3559
    /**
3560
     * Deprecate the sensei_lesson_archive_header hook but keep it
3561
     * active for backwards compatibility.
3562
     *
3563
     * @deprecated since 1.9.0
3564
     */
3565
    public static function deprecate_sensei_lesson_archive_header_hook(){
3566
3567
        sensei_do_deprecated_action('sensei_lesson_archive_header', '1.9.0', 'sensei_loop_lesson_inside_before');
3568
3569
    }
3570
3571
    /**
3572
     * Outputs the the lesson archive header.
3573
     *
3574
     * @since  1.9.0
3575
     * @return void
3576
     */
3577
    public function the_archive_header( ) {
3578
3579
        $before_html = '<header class="archive-header"><h1>';
3580
        $after_html = '</h1></header>';
3581
        $html = $before_html .  __( 'Lessons Archive', 'woothemes-sensei' ) . $after_html;
3582
3583
        echo apply_filters( 'sensei_lesson_archive_title', $html );
3584
3585
    } // sensei_course_archive_header()
3586
3587
    /**
3588
     * Output the title for the single lesson page
3589
     *
3590
     * @global $post
3591
     * @since 1.9.0
3592
     */
3593
    public static function the_title(){
3594
3595
        global $post;
3596
3597
        ?>
3598
        <header>
3599
3600
            <h1>
3601
3602
                <?php
3603
                /**
3604
                 * Filter documented in class-sensei-messages.php the_title
3605
                 */
3606
                echo apply_filters( 'sensei_single_title', get_the_title( $post ), $post->post_type );
3607
                ?>
3608
3609
            </h1>
3610
3611
        </header>
3612
3613
        <?php
3614
3615
    }//the_title
3616
3617
    /**
3618
     * Flush the rewrite rules for a lesson post type
3619
     *
3620
     * @since 1.9.0
3621
     *
3622
     * @param $post_id
3623
     */
3624 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...
3625
3626
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
3627
3628
            return;
3629
3630
        }
3631
3632
3633
        if( 'lesson' == get_post_type( $post_id )  ){
3634
3635
            Sensei()->initiate_rewrite_rules_flush();
3636
3637
        }
3638
3639
    }
3640
3641
    /**
3642
     * Output the quiz specific buttons and messaging on the single lesson page
3643
     *
3644
     *
3645
     * @since 1.0.0 moved here from frontend class
3646
     *
3647
     * @param int $lesson_id
3648
     * @param int $user_id
3649
     */
3650
    public static function footer_quiz_call_to_action( $lesson_id = 0, $user_id = 0 ) {
3651
3652
3653
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3654
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3655
        $lesson_prerequisite       = (int) get_post_meta( $lesson_id, '_lesson_prerequisite', true );
3656
        $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...
3657
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3658
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3659
        $show_actions              = is_user_logged_in() ? true : false;
3660
3661
        if( intval( $lesson_prerequisite ) > 0 ) {
3662
3663
            // If the user hasn't completed the prereq then hide the current actions
3664
            $show_actions = Sensei_Utils::user_completed_lesson( $lesson_prerequisite, $user_id );
3665
3666
        }
3667
        ?>
3668
3669
        <footer>
3670
3671
            <?php
3672
            if( $show_actions && $quiz_id && Sensei()->access_settings() ) {
3673
3674
                $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3675
                if( $has_quiz_questions ) {
3676
                    ?>
3677
3678
                    <p>
3679
3680
                        <a class="button"
3681
                           href="<?php echo esc_url_raw( get_permalink( $quiz_id ) ); ?>"
3682
                           title="<?php _e( 'View the Lesson Quiz', 'woothemes-sensei'  ); ?>">
3683
3684
                            <?php  _e( 'View the Lesson Quiz', 'woothemes-sensei' ); ?>
3685
3686
                        </a>
3687
3688
                    </p>
3689
3690
                    <?php
3691
                }
3692
3693
            } // End If Statement
3694
3695
            if ( $show_actions && ! $has_user_completed_lesson ) {
3696
3697
                sensei_complete_lesson_button();
3698
3699
            } elseif( $show_actions ) {
3700
3701
                sensei_reset_lesson_button();
3702
3703
            } // End If Statement
3704
            ?>
3705
3706
        </footer>
3707
3708
        <?php
3709
    } // End sensei_lesson_quiz_meta()
3710
3711
    /**
3712
     * Show the lesson comments. This should be used in the loop.
3713
     *
3714
     * @since 1.9.0
3715
     */
3716
    public static function output_comments(){
3717
3718
        if( ! is_user_logged_in() ){
3719
            return;
3720
        }
3721
3722
        $pre_requisite_complete = Sensei()->lesson->is_prerequisite_complete( get_the_ID(), get_current_user_id() );
3723
        $course_id = Sensei()->lesson->get_course_id( get_the_ID() );
3724
        $allow_comments = Sensei()->settings->settings[ 'lesson_comments' ];
3725
        $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 3723 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...
3726
3727
        $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...
3728
3729
        if (  $lesson_allow_comments || is_singular( 'sensei_message' ) ) {
3730
3731
            comments_template();
3732
3733
        } // End If Statement
3734
3735
    } //output_comments
3736
3737
    /**
3738
     * Display the leeson quiz status if it should be shown
3739
     *
3740
     * @param int $lesson_id defaults to the global lesson id
3741
     * @param int $user_id defaults to the current user id
3742
     *
3743
     * @since 1.9.0
3744
     */
3745
    public static function user_lesson_quiz_status_message( $lesson_id = 0, $user_id = 0){
3746
3747
        $lesson_id                 =  empty( $lesson_id ) ?  get_the_ID() : $lesson_id;
3748
        $user_id                   = empty( $lesson_id ) ?  get_current_user_id() : $user_id;
3749
        $lesson_course_id          = (int) get_post_meta( $lesson_id, '_lesson_course', true );
3750
        $quiz_id                   = Sensei()->lesson->lesson_quizzes( $lesson_id );
3751
        $has_user_completed_lesson = Sensei_Utils::user_completed_lesson( intval( $lesson_id ), $user_id );
3752
3753
3754
        if ( $quiz_id && is_user_logged_in()
3755
            && 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...
3756
3757
            $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...
3758
            $has_quiz_questions = get_post_meta( $lesson_id, '_quiz_has_questions', true );
3759
3760
            // Display lesson quiz status message
3761
            if ( $has_user_completed_lesson || $has_quiz_questions ) {
3762
                $status = Sensei_Utils::sensei_user_quiz_status_message( $lesson_id, $user_id, true );
3763
                echo '<div class="sensei-message ' . $status['box_class'] . '">' . $status['message'] . '</div>';
3764
                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...
3765
                   // 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...
3766
                } // End If Statement
3767
            } // End If Statement
3768
3769
        }
3770
3771
    }
3772
3773
} // End Class
3774
3775
/**
3776
 * Class WooThemes_Sensei_Lesson
3777
 * for backward compatibility
3778
 * @since 1.9.0
3779
 */
3780
class WooThemes_Sensei_Lesson extends Sensei_Lesson{}
3781