Issues (896)

Security Analysis    not enabled

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

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

includes/class-sensei-grading.php (12 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4
/**
5
 * Sensei Grading Class
6
 *
7
 * All functionality pertaining to the Admin Grading in Sensei.
8
 *
9
 * @package Assessment
10
 * @author Automattic
11
 *
12
 * @since 1.3.0
13
 */
14
class Sensei_Grading {
15
16
	public $name;
17
	public $file;
18
	public $page_slug;
19
20
	/**
21
	 * Constructor
22
	 * @since  1.3.0
23
     *
24
     * @param $file
25
	 */
26
	public function __construct ( $file ) {
27
		$this->name = __( 'Grading', 'woothemes-sensei' );
28
		$this->file = $file;
29
		$this->page_slug = 'sensei_grading';
30
31
		// Admin functions
32 View Code Duplication
		if ( is_admin() ) {
33
			add_action( 'admin_menu', array( $this, 'grading_admin_menu' ), 20);
34
			add_action( 'grading_wrapper_container', array( $this, 'wrapper_container'  ) );
35
			if ( isset( $_GET['page'] ) && ( $_GET['page'] == $this->page_slug ) ) {
36
				add_action( 'admin_print_scripts', array( $this, 'enqueue_scripts' ) );
37
				add_action( 'admin_print_styles', array( $this, 'enqueue_styles' ) );
38
			}
39
40
			add_action( 'admin_init', array( $this, 'admin_process_grading_submission' ) );
41
42
			add_action( 'admin_notices', array( $this, 'add_grading_notices' ) );
43
//			add_action( 'sensei_grading_notices', array( $this, 'sensei_grading_notices' ) );
44
		} // End If Statement
45
46
		// Ajax functions
47
		if ( is_admin() ) {
48
			add_action( 'wp_ajax_get_lessons_dropdown', array( $this, 'get_lessons_dropdown' ) );
49
			add_action( 'wp_ajax_get_redirect_url', array( $this, 'get_redirect_url' ) );
50
		} // End If Statement
51
	} // End __construct()
52
53
	/**
54
	 * grading_admin_menu function.
55
	 * @since  1.3.0
56
	 * @access public
57
	 * @return void
58
	 */
59 View Code Duplication
	public function grading_admin_menu() {
60
		global $menu;
61
62
		if ( current_user_can( 'manage_sensei_grades' ) ) {
63
			$grading_page = add_submenu_page('sensei', __('Grading', 'woothemes-sensei'),  __('Grading', 'woothemes-sensei') , 'manage_sensei_grades', $this->page_slug, array( $this, 'grading_page' ) );
64
		}
65
66
	} // End grading_admin_menu()
67
68
	/**
69
	 * enqueue_scripts function.
70
	 *
71
	 * @description Load in JavaScripts where necessary.
72
	 * @access public
73
	 * @since 1.3.0
74
	 * @return void
75
	 */
76
	public function enqueue_scripts () {
77
78
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
79
80
		// Load Grading JS
81
		wp_enqueue_script( 'sensei-grading-general', Sensei()->plugin_url . 'assets/js/grading-general' . $suffix . '.js', array( 'jquery' ), Sensei()->version );
82
83
	} // End enqueue_scripts()
84
85
	/**
86
	 * enqueue_styles function.
87
	 *
88
	 * @description Load in CSS styles where necessary.
89
	 * @access public
90
	 * @since 1.0.0
91
	 * @return void
92
	 */
93
	public function enqueue_styles () {
94
95
		wp_enqueue_style( Sensei()->token . '-admin' );
96
97
		wp_enqueue_style( 'woothemes-sensei-settings-api', Sensei()->plugin_url . 'assets/css/settings.css', '', Sensei()->version );
98
99
	} // End enqueue_styles()
100
101
	/**
102
	 * load_data_table_files loads required files for Grading
103
	 * @since  1.3.0
104
	 * @return void
105
	 */
106
	public function load_data_table_files() {
107
108
		// Load Grading Classes
109
		$classes_to_load = array(	'list-table',
110
									'grading-main',
111
									'grading-user-quiz'
112
									);
113
		foreach ( $classes_to_load as $class_file ) {
114
			Sensei()->load_class( $class_file );
115
		} // End For Loop
116
	} // End load_data_table_files()
117
118
	/**
119
	 * load_data_object creates new instance of class
120
	 * @since  1.3.0
121
	 * @param  string  $name          Name of class
122
	 * @param  integer $data          constructor arguments
123
	 * @param  undefined  $optional_data optional constructor arguments
124
	 * @return object                 class instance object
125
	 */
126 View Code Duplication
	public function load_data_object( $name = '', $data = 0, $optional_data = null ) {
127
		// Load Analysis data
128
		$object_name = 'WooThemes_Sensei_Grading_' . $name;
129
		if ( is_null($optional_data) ) {
130
			$sensei_grading_object = new $object_name( $data );
131
		}
132
		else {
133
			$sensei_grading_object = new $object_name( $data, $optional_data );
134
		} // End If Statement
135
		if ( 'Main' == $name ) {
136
			$sensei_grading_object->prepare_items();
137
		} // End If Statement
138
		return $sensei_grading_object;
139
	} // End load_data_object()
140
141
	/**
142
	 * grading_page function.
143
	 * @since 1.3.0
144
	 * @access public
145
	 * @return void
146
	 */
147
	public function grading_page() {
148
149
		if ( isset( $_GET['quiz_id'] ) && 0 < intval( $_GET['quiz_id'] ) && isset( $_GET['user'] ) && 0 < intval( $_GET['user'] ) ) {
150
			$this->grading_user_quiz_view();
151
		}
152
		else {
153
			$this->grading_default_view();
154
		} // End If Statement
155
	} // End grading_page()
156
157
	/**
158
	 * grading_default_view default view for grading page
159
	 * @since  1.3.0
160
	 * @return void
161
	 */
162
	public function grading_default_view() {
163
164
		// Load Grading data
165
		if( !empty( $_GET['course_id'] ) ) {
166
			$course_id = intval( $_GET['course_id'] );
167
		}
168
		if( !empty( $_GET['lesson_id'] ) ) {
169
			$lesson_id = intval( $_GET['lesson_id'] );
170
		}
171
		if( !empty( $_GET['user_id'] ) ) {
172
			$user_id = intval( $_GET['user_id'] );
173
		}
174
		if( !empty( $_GET['view'] ) ) {
175
			$view = esc_html( $_GET['view'] );
176
		}
177
		$sensei_grading_overview = $this->load_data_object( 'Main', compact( 'course_id', 'lesson_id', 'user_id', 'view' ) );
178
179
		// Wrappers
180
		do_action( 'grading_before_container' );
181
		do_action( 'grading_wrapper_container', 'top' );
182
		$this->grading_headers();
183
		?>
184
		<div id="poststuff" class="sensei-grading-wrap">
185
			<div class="sensei-grading-main">
186
				<?php $sensei_grading_overview->display(); ?>
187
			</div>
188
			<div class="sensei-grading-extra">
189
				<?php do_action( 'sensei_grading_extra' ); ?>
190
			</div>
191
		</div>
192
		<?php
193
		do_action( 'grading_wrapper_container', 'bottom' );
194
		do_action( 'grading_after_container' );
195
	} // End grading_default_view()
196
197
	/**
198
	 * grading_user_quiz_view user quiz answers view for grading page
199
	 * @since  1.2.0
200
	 * @return void
201
	 */
202 View Code Duplication
	public function grading_user_quiz_view() {
203
204
		// Load Grading data
205
		$user_id = 0;
206
		$quiz_id = 0;
207
		if( isset( $_GET['user'] ) ) {
208
			$user_id = intval( $_GET['user'] );
209
		}
210
		if( isset( $_GET['quiz_id'] ) ) {
211
			$quiz_id = intval( $_GET['quiz_id'] );
212
		}
213
		$sensei_grading_user_profile = $this->load_data_object( 'User_Quiz', $user_id, $quiz_id );
214
		// Wrappers
215
		do_action( 'grading_before_container' );
216
		do_action( 'grading_wrapper_container', 'top' );
217
		$this->grading_headers( array( 'nav' => 'user_quiz' ) );
218
		?>
219
		<div id="poststuff" class="sensei-grading-wrap user-profile">
220
			<div class="sensei-grading-main">
221
				<?php // do_action( 'sensei_grading_notices' ); ?>
222
				<?php $sensei_grading_user_profile->display(); ?>
223
			</div>
224
		</div>
225
		<?php
226
		do_action( 'grading_wrapper_container', 'bottom' );
227
		do_action( 'grading_after_container' );
228
	} // End grading_user_quiz_view()
229
230
	/**
231
	 * Outputs Grading general headers
232
	 * @since  1.3.0
233
     * @param array $args
234
	 * @return void
235
	 */
236 View Code Duplication
	public function grading_headers( $args = array( 'nav' => 'default' ) ) {
237
238
		$function = 'grading_' . $args['nav'] . '_nav';
239
		$this->$function();
240
		?>
241
			<p class="powered-by-woo"><?php _e( 'Powered by', 'woothemes-sensei' ); ?><a href="http://www.woothemes.com/" title="WooThemes"><img src="<?php echo Sensei()->plugin_url; ?>assets/images/woothemes.png" alt="WooThemes" /></a></p>
242
		<?php
243
		do_action( 'sensei_grading_after_headers' );
244
	} // End grading_headers()
245
246
	/**
247
	 * wrapper_container wrapper for Grading area
248
	 * @since  1.3.0
249
	 * @param $which string
250
	 * @return void
251
	 */
252
	public function wrapper_container( $which ) {
253
		if ( 'top' == $which ) {
254
			?><div id="woothemes-sensei" class="wrap woothemes-sensei"><?php
255
		} elseif ( 'bottom' == $which ) {
256
			?></div><!--/#woothemes-sensei--><?php
257
		} // End If Statement
258
	} // End wrapper_container()
259
260
	/**
261
	 * Default nav area for Grading
262
	 * @since  1.3.0
263
	 * @return void
264
	 */
265
	public function grading_default_nav() {
266
		global  $wp_version;
267
268
		$title = sprintf( '<a href="%s">%s</a>', esc_url(add_query_arg( array( 'page' => $this->page_slug ), admin_url( 'admin.php' ) ) ), esc_html( $this->name ) );
269
		if ( isset( $_GET['course_id'] ) ) { 
270
			$course_id = intval( $_GET['course_id'] );
271 View Code Duplication
			if ( version_compare($wp_version, '4.1', '>=') ) {
272
				$url = add_query_arg( array( 'page' => $this->page_slug, 'course_id' => $course_id ), admin_url( 'admin.php' ) );
273
				$title .= sprintf( '&nbsp;&nbsp;<span class="course-title">&gt;&nbsp;&nbsp;<a href="%s">%s</a></span>', esc_url( $url ), get_the_title( $course_id ) );
274
			}
275
			else {
276
				$title .= sprintf( '&nbsp;&nbsp;<span class="course-title">&gt;&nbsp;&nbsp;%s</span>', get_the_title( $course_id ) ); 
277
			}
278
		}
279 View Code Duplication
		if ( isset( $_GET['lesson_id'] ) ) { 
280
			$lesson_id = intval( $_GET['lesson_id'] );
281
			$title .= '&nbsp;&nbsp;<span class="lesson-title">&gt;&nbsp;&nbsp;' . get_the_title( intval( $lesson_id ) ) . '</span>'; 
282
		}
283 View Code Duplication
		if ( isset( $_GET['user_id'] ) && 0 < intval( $_GET['user_id'] ) ) {
284
285
            $user_name = Sensei_Learner::get_full_name( $_GET['user_id'] );
286
			$title .= '&nbsp;&nbsp;<span class="user-title">&gt;&nbsp;&nbsp;' . $user_name . '</span>';
287
288
		} // End If Statement
289
		?>
290
			<h2><?php echo apply_filters( 'sensei_grading_nav_title', $title ); ?></h2>
291
		<?php
292
	} // End grading_default_nav()
293
294
	/**
295
	 * Nav area for Grading specific users' quiz answers
296
	 * @since  1.3.0
297
	 * @return void
298
	 */
299
	public function grading_user_quiz_nav() {
300
		global  $wp_version;
301
302
		$title = sprintf( '<a href="%s">%s</a>', add_query_arg( array( 'page' => $this->page_slug ), admin_url( 'admin.php' ) ), esc_html( $this->name ) );
303
		if ( isset( $_GET['quiz_id'] ) ) { 
304
			$quiz_id = intval( $_GET['quiz_id'] );
305
			$lesson_id = get_post_meta( $quiz_id, '_quiz_lesson', true );
306
			$course_id = get_post_meta( $lesson_id, '_lesson_course', true );
307 View Code Duplication
			if ( version_compare($wp_version, '4.1', '>=') ) {
308
				$url = add_query_arg( array( 'page' => $this->page_slug, 'course_id' => $course_id ), admin_url( 'admin.php' ) );
309
				$title .= sprintf( '&nbsp;&nbsp;<span class="course-title">&gt;&nbsp;&nbsp;<a href="%s">%s</a></span>', esc_url( $url ), get_the_title( $course_id ) );
310
			}
311
			else {
312
				$title .= sprintf( '&nbsp;&nbsp;<span class="course-title">&gt;&nbsp;&nbsp;%s</span>', get_the_title( $course_id ) ); 
313
			}
314
			$url = add_query_arg( array( 'page' => $this->page_slug, 'lesson_id' => $lesson_id ), admin_url( 'admin.php' ) );
315
			$title .= sprintf( '&nbsp;&nbsp;<span class="lesson-title">&gt;&nbsp;&nbsp;<a href="%s">%s</a></span>', esc_url( $url ), get_the_title( $lesson_id ) );
316
		}
317 View Code Duplication
		if ( isset( $_GET['user'] ) && 0 < intval( $_GET['user'] ) ) {
318
319
            $user_name = Sensei_Learner::get_full_name( $_GET['user'] );
320
			$title .= '&nbsp;&nbsp;<span class="user-title">&gt;&nbsp;&nbsp;' . $user_name . '</span>';
321
322
		} // End If Statement
323
		?>
324
			<h2><?php echo apply_filters( 'sensei_grading_nav_title', $title ); ?></h2>
325
		<?php
326
	} // End grading_user_quiz_nav()
327
328
	/**
329
	 * Return array of valid statuses for either Course or Lesson
330
	 * @since  1.7.0
331
	 * @return array
332
	 */
333
	public function get_stati( $type ) {
334
		$statuses = array();
335
		switch( $type ) {
336
			case 'course' :
337
				$statuses = array(
338
					'in-progress',
339
					'complete',
340
				);
341
				break;
342
343
			case 'lesson' :
344
				$statuses = array(
345
					'in-progress',
346
					'complete',
347
					'ungraded',
348
					'graded',
349
					'passed',
350
					'failed',
351
				);
352
				break;
353
354
		}
355
		return $statuses;
356
	}
357
358
	/**
359
	 * Count the various statuses for Course or Lesson
360
	 * Very similar to get_comment_count()
361
	 * @since  1.7.0
362
	 * @param  array $args (default: array())
363
	 * @return object
364
	 */
365
	public function count_statuses( $args = array() ) {
366
		global  $wpdb;
367
368
        /**
369
         * Filter fires inside Sensei_Grading::count_statuses
370
         *
371
         * Alter the the post_in array to determine which posts the
372
         * comment query should be limited to.
373
         * @since 1.8.0
374
         * @param array $args
375
         */
376
        $args = apply_filters( 'sensei_count_statuses_args', $args );
377
378
		if ( 'course' == $args['type'] ) {
379
			$type = 'sensei_course_status';
380
		}
381
		else {
382
			$type = 'sensei_lesson_status';
383
		}
384
		$cache_key = 'sensei-' . $args['type'] . '-statuses';
385
386
		$query = "SELECT comment_approved, COUNT( * ) AS total FROM {$wpdb->comments} WHERE comment_type = %s ";
387
388
        // Restrict to specific posts
389 View Code Duplication
		if ( isset( $args['post__in'] ) && !empty( $args['post__in'] ) && is_array( $args['post__in'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
390
			$query .= ' AND comment_post_ID IN (' . implode( ',', array_map( 'absint', $args['post__in'] ) ) . ')';
391
		}
392
		elseif ( !empty( $args['post_id'] ) ) {
393
			$query .= $wpdb->prepare( ' AND comment_post_ID = %d', $args['post_id'] );
394
		}
395
		// Restrict to specific users
396 View Code Duplication
		if ( isset( $args['user_id'] ) && is_array( $args['user_id'] ) ) {
397
			$query .= ' AND user_id IN (' . implode( ',', array_map( 'absint', $args['user_id'] ) ) . ')';
398
		}
399
		elseif ( !empty( $args['user_id'] ) ) {
400
			$query .= $wpdb->prepare( ' AND user_id = %d', $args['user_id'] );
401
		}
402
		$query .= ' GROUP BY comment_approved';
403
404
		$counts = wp_cache_get( $cache_key, 'counts' );
405
		if ( false === $counts ) {
406
			$sql = $wpdb->prepare( $query, $type );
407
			$results = (array) $wpdb->get_results( $sql, ARRAY_A );
408
			$counts = array_fill_keys( $this->get_stati( $type ), 0 );
409
410
			foreach ( $results as $row ) {
411
				$counts[ $row['comment_approved'] ] = $row['total'];
412
			}
413
			wp_cache_set( $cache_key, $counts, 'counts' );
414
		}
415
416
		if( ! isset( $counts['graded'] ) ) {
417
			$counts['graded'] = 0;
418
		}
419
420
		if( ! isset( $counts['ungraded'] ) ) {
421
			$counts['ungraded'] = 0;
422
		}
423
424
		if( ! isset( $counts['passed'] ) ) {
425
			$counts['passed'] = 0;
426
		}
427
428
		if( ! isset( $counts['failed'] ) ) {
429
			$counts['failed'] = 0;
430
		}
431
432
		if( ! isset( $counts['in-progress'] ) ) {
433
			$counts['in-progress'] = 0;
434
		}
435
436
		if( ! isset( $counts['complete'] ) ) {
437
			$counts['complete'] = 0;
438
		}
439
440
		return apply_filters( 'sensei_count_statuses', $counts, $type );
441
	} // End sensei_count_statuses()
442
443
	/**
444
	 * Build the Courses dropdown for return in AJAX
445
	 * @since  1.7.0
446
	 * @return string
447
	 */
448
	public function courses_drop_down_html( $selected_course_id = 0 ) {
449
450
		$html = '';
451
452
		$course_args = array(   'post_type'         => 'course',
453
								'posts_per_page'       => -1,
454
								'orderby'           => 'title',
455
								'order'             => 'ASC',
456
								'post_status'       => 'any',
457
								'suppress_filters'  => 0,
458
								'fields'            => 'ids',
459
							);
460
		$courses = get_posts( apply_filters( 'sensei_grading_filter_courses', $course_args ) );
461
462
		$html .= '<option value="">' . __( 'Select a course', 'woothemes-sensei' ) . '</option>';
463 View Code Duplication
		if ( count( $courses ) > 0 ) {
464
			foreach ($courses as $course_id){
465
				$html .= '<option value="' . esc_attr( absint( $course_id ) ) . '" ' . selected( $course_id, $selected_course_id, false ) . '>' . esc_html( get_the_title( $course_id ) ) . '</option>' . "\n";
466
			} // End For Loop
467
		} // End If Statement
468
469
		return $html;
470
	} // End lessons_drop_down_html()
471
472
	/**
473
	 * Build the Lessons dropdown for return in AJAX
474
	 * @since  1.?
475
	 * @return string
476
	 */
477
	public function get_lessons_dropdown() {
478
479
		$posts_array = array();
480
481
		// Parse POST data
482
		$data = $_POST['data'];
483
		$course_data = array();
484
		parse_str($data, $course_data);
485
486
		$course_id = intval( $course_data['course_id'] );
487
488
		$html = $this->lessons_drop_down_html( $course_id );
489
490
		echo $html;
491
		die(); // WordPress may print out a spurious zero without this can be particularly bad if using JSON
492
	}
493
494
	public function lessons_drop_down_html( $course_id = 0, $selected_lesson_id = 0 ) {
495
496
		$html = '';
497
		if ( 0 < intval( $course_id ) ) {
498
499
			$lesson_args = array( 'post_type'       => 'lesson',
500
								'posts_per_page'       => -1,
501
								'orderby'           => 'title',
502
								'order'             => 'ASC',
503
								'meta_key'          => '_lesson_course',
504
								'meta_value'        => $course_id,
505
								'post_status'       => 'publish',
506
								'suppress_filters'  => 0,
507
								'fields'            => 'ids',
508
								);
509
			$lessons = get_posts( apply_filters( 'sensei_grading_filter_lessons', $lesson_args ) );
510
511
			$html .= '<option value="">' . __( 'Select a lesson', 'woothemes-sensei' ) . '</option>';
512 View Code Duplication
			if ( count( $lessons ) > 0 ) {
513
				foreach ( $lessons as $lesson_id ){
514
					$html .= '<option value="' . esc_attr( absint( $lesson_id ) ) . '" ' . selected( $lesson_id, $selected_lesson_id, false ) . '>' . esc_html( get_the_title( $lesson_id ) ) . '</option>' . "\n";
515
				} // End For Loop
516
			} // End If Statement
517
518
		} // End If Statement
519
520
		return $html;
521
	} // End lessons_drop_down_html()
522
523
    /**
524
     * The process grading function handles admin grading submissions.
525
     *
526
     * This function is hooked on to admin_init. It simply accepts
527
     * the grades as the Grader selected theme and saves the total grade and
528
     * individual question grades.
529
     *
530
     * @return bool
531
     */
532
    public function admin_process_grading_submission() {
533
534
        // NEEDS REFACTOR/OPTIMISING, such as combining the various meta data stored against the sensei_user_answer entry
535
        if( ! isset( $_POST['sensei_manual_grade'] )
536
            || ! wp_verify_nonce( $_POST['_wp_sensei_manual_grading_nonce'], 'sensei_manual_grading' )
537
            || ! isset( $_GET['quiz_id'] )
538
            || $_GET['quiz_id'] != $_POST['sensei_manual_grade'] ) {
539
540
            return false; //exit and do not grade
541
542
        }
543
544
        $quiz_id = $_GET['quiz_id'];
545
        $user_id = $_GET['user'];
546
547
548
        $questions = Sensei_Utils::sensei_get_quiz_questions( $quiz_id );
549
        $quiz_lesson_id =  Sensei()->quiz->get_lesson_id( $quiz_id );
550
        $quiz_grade = 0;
551
        $count = 0;
552
        $quiz_grade_total = $_POST['quiz_grade_total'];
553
        $all_question_grades = array();
554
        $all_answers_feedback = array();
555
556
        foreach( $questions as $question ) {
557
558
            ++$count;
559
            $question_id = $question->ID;
560
561
            if( isset( $_POST[ 'question_' . $question_id ] ) ) {
562
563
                $question_grade = 0;
564
                if( $_POST[ 'question_' . $question_id ] == 'right' ) {
565
566
                    $question_grade = $_POST[ 'question_' . $question_id . '_grade' ];
567
568
                }
569
570
                // add data to the array that will, after the loop, be stored on the lesson status
571
                $all_question_grades[ $question_id ] = $question_grade;
572
573
                // tally up the total quiz grade
574
                $quiz_grade += $question_grade;
575
576
            } // endif
577
578
            // Question answer feedback / notes
579
            $question_feedback = '';
580
            if( isset( $_POST[ 'questions_feedback' ][ $question_id ] ) ){
581
582
                $question_feedback = wp_unslash( $_POST[ 'questions_feedback' ][ $question_id ] );
583
584
            }
585
            $all_answers_feedback[ $question_id ] = $question_feedback;
586
587
        } // end for each $questions
588
589
        //store all question grades on the lesson status
590
        Sensei()->quiz->set_user_grades( $all_question_grades, $quiz_lesson_id , $user_id );
591
592
        //store the feedback from grading
593
        Sensei()->quiz->save_user_answers_feedback( $all_answers_feedback, $quiz_lesson_id , $user_id );
594
595
        // $_POST['all_questions_graded'] is set when all questions have been graded
596
        // in the class sensei grading user quiz -> display()
597
        if( $_POST['all_questions_graded'] == 'yes' ) {
598
599
            // set the users total quiz grade
600
			if ( 0 < intval( $quiz_grade_total ) ) {
601
            $grade = abs( round( ( doubleval( $quiz_grade ) * 100 ) / ( $quiz_grade_total ), 2 ) );
602
			}
603
			else {
604
				$grade = 0;
605
			}
606
            Sensei_Utils::sensei_grade_quiz( $quiz_id, $grade, $user_id );
607
608
            // Duplicating what Frontend->sensei_complete_quiz() does
609
            $pass_required = get_post_meta( $quiz_id, '_pass_required', true );
610
            $quiz_passmark = abs( round( doubleval( get_post_meta( $quiz_id, '_quiz_passmark', true ) ), 2 ) );
611
            $lesson_metadata = array();
612 View Code Duplication
            if ( $pass_required ) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
613
                // Student has reached the pass mark and lesson is complete
614
                if ( $quiz_passmark <= $grade ) {
615
                    $lesson_status = 'passed';
616
                }
617
                else {
618
                    $lesson_status = 'failed';
619
                } // End If Statement
620
            }
621
            // Student only has to partake the quiz
622
            else {
623
                $lesson_status = 'graded';
624
            }
625
            $lesson_metadata['grade'] = $grade; // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz()" above
626
627
            Sensei_Utils::update_lesson_status( $user_id, $quiz_lesson_id, $lesson_status, $lesson_metadata );
628
629
            if(  in_array( $lesson_status, array( 'passed', 'graded'  ) ) ) {
630
631
                /**
632
                 * Summary.
633
                 *
634
                 * Description.
635
                 *
636
                 * @since 1.7.0
637
                 *
638
                 * @param int  $user_id
639
                 * @param int $quiz_lesson_id
640
                 */
641
                do_action( 'sensei_user_lesson_end', $user_id, $quiz_lesson_id );
642
643
            } // end if in_array
644
645
        }// end if $_POST['all_que...
646
647
        if( isset( $_POST['sensei_grade_next_learner'] ) && strlen( $_POST['sensei_grade_next_learner'] ) > 0 ) {
648
649
            $load_url = add_query_arg( array( 'message' => 'graded' ) );
650
651
        } elseif ( isset( $_POST['_wp_http_referer'] ) ) {
652
653
            $load_url = add_query_arg( array( 'message' => 'graded' ), $_POST['_wp_http_referer'] );
654
655
        } else {
656
657
            $load_url = add_query_arg( array( 'message' => 'graded' ) );
658
659
        }
660
661
        wp_safe_redirect( esc_url_raw( $load_url ) );
662
        exit;
663
664
    } // end admin_process_grading_submission
665
666
	public function get_redirect_url() {
667
		// Parse POST data
668
		$data = $_POST['data'];
669
		$lesson_data = array();
670
		parse_str($data, $lesson_data);
671
672
		$lesson_id = intval( $lesson_data['lesson_id'] );
673
		$course_id = intval( $lesson_data['course_id'] );
674
		$grading_view = sanitize_text_field( $lesson_data['view'] );
675
676
		$redirect_url = '';
677
		if ( 0 < $lesson_id && 0 < $course_id ) {
678
			$redirect_url = esc_url_raw( apply_filters( 'sensei_ajax_redirect_url', add_query_arg( array( 'page' => $this->page_slug, 'lesson_id' => $lesson_id, 'course_id' => $course_id, 'view' => $grading_view ), admin_url( 'admin.php' ) ) ) );
679
		} // End If Statement
680
681
		echo $redirect_url;
682
		die();
683
	}
684
685
	public function add_grading_notices() {
686
		if( isset( $_GET['page'] ) && $this->page_slug == $_GET['page'] && isset( $_GET['message'] ) && $_GET['message'] ) {
687
			if( 'graded' == $_GET['message'] ) {
688
				$msg = array(
689
					'updated',
690
					__( 'Quiz Graded Successfully!', 'woothemes-sensei' ),
691
				);
692
			}
693
			?>
694
			<div class="grading-notice <?php echo $msg[0]; ?>">
695
				<p><?php echo $msg[1]; ?></p>
696
			</div>
697
			<?php
698
		}
699
	}
700
701
	public function sensei_grading_notices() {
702
		if ( isset( $_GET['action'] ) && 'graded' == $_GET['action'] ) {
703
			echo '<div class="grading-notice updated">';
704
				echo '<p>' . __( 'Quiz Graded Successfully!', 'woothemes-sensei' ) . '</p>';
705
			echo '</div>';
706
		} // End If Statement
707
	} // End sensei_grading_notices()
708
709
    /**
710
     * Grade quiz automatically
711
     *
712
     * This function grades each question automatically if there all questions are auto gradable. If not
713
     * the quiz will not be auto gradable.
714
     *
715
     * @since 1.7.4
716
     *
717
     * @param  integer $quiz_id         ID of quiz
718
     * @param  array $submitted questions id ans answers {
719
     *          @type int $question_id
720
     *          @type mixed $answer
721
     * }
722
     * @param  integer $total_questions Total questions in quiz (not used)
723
     * @param string $quiz_grade_type Optional defaults to auto
724
     *
725
     * @return int $quiz_grade total sum of all question grades
726
     */
727
    public static function grade_quiz_auto( $quiz_id = 0, $submitted = array(), $total_questions = 0, $quiz_grade_type = 'auto' ) {
0 ignored issues
show
The parameter $total_questions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
728
729
        if( ! ( intval( $quiz_id ) > 0 )  || ! $submitted
0 ignored issues
show
Bug Best Practice introduced by
The expression $submitted of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
730
            || $quiz_grade_type != 'auto' ) {
731
            return false; // exit early
732
        }
733
734
735
        $user_id = get_current_user_id();
736
        $lesson_id =  Sensei()->quiz->get_lesson_id(  $quiz_id ) ;
737
        $quiz_autogradable = true;
738
739
        /**
740
         * Filter the types of question types that can be automatically graded.
741
         *
742
         * This filter fires inside the auto grade quiz function and provides you with the default list.
743
         *
744
         * @param array {
745
         *      'multiple-choice',
746
         *      'boolean',
747
         *      'gap-fill'.
748
         * }
749
         */
750
        $autogradable_question_types = apply_filters( 'sensei_autogradable_question_types', array( 'multiple-choice', 'boolean', 'gap-fill' ) );
751
752
        $grade_total = 0;
753
        $all_question_grades = array();
754
        foreach( $submitted as $question_id => $answer ) {
755
756
            // check if the question is autogradable, either by type, or because the grade is 0
757
            $question_type = Sensei()->question->get_question_type( $question_id );
758
			$achievable_grade = Sensei()->question->get_question_grade( $question_id );
759
			// Question has a zero grade, so skip grading
760
			if ( 0 == $achievable_grade ) {
761
				$all_question_grades[ $question_id ] = $achievable_grade;
762
			}
763
            elseif ( in_array( $question_type, $autogradable_question_types ) ) {
764
                // Get user question grade
765
                $question_grade = Sensei_Utils::sensei_grade_question_auto( $question_id, $question_type, $answer, $user_id );
0 ignored issues
show
Deprecated Code introduced by
The method Sensei_Utils::sensei_grade_question_auto() has been deprecated with message: since 1.7.4 use WooThemes_Sensei_Grading::grade_question_auto instead

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...
766
                $all_question_grades[ $question_id ] = $question_grade;
767
                $grade_total += $question_grade;
768
769
            } else {
770
771
                // There is a question that cannot be autograded
772
                $quiz_autogradable = false;
773
774
            } // end if in_array( $question_type...
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
775
776
        }// end for each question
777
778
        // Only if the whole quiz was autogradable do we set a grade
779
        if ( $quiz_autogradable ) {
780
781
            $quiz_total = Sensei_Utils::sensei_get_quiz_total( $quiz_id );
782
			// Check for zero total from grades
783
			if ( 0 < $quiz_total ) {
784
            $grade = abs( round( ( doubleval( $grade_total ) * 100 ) / ( $quiz_total ), 2 ) );
785
			}
786
			else {
787
				$grade = 0;
788
			}
789
            Sensei_Utils::sensei_grade_quiz( $quiz_id, $grade, $user_id, $quiz_grade_type );
790
791
        } else {
792
793
            $grade = new WP_Error( 'autograde', __( 'This quiz is not able to be automatically graded.', 'woothemes-sensei' ) );
794
795
        }
796
797
        // store the auto gradable grades. If the quiz is not auto gradable the grades can be use as the default
798
        // when doing manual grading.
799
        Sensei()->quiz-> set_user_grades( $all_question_grades, $lesson_id, $user_id );
800
801
        return $grade;
802
803
    } // End grade_quiz_auto()
804
805
    /**
806
     * Grade question automatically
807
     *
808
     * This function checks the question typ and then grades it accordingly.
809
     *
810
     * @since 1.7.4
811
     *
812
     * @param integer $question_id
813
     * @param string $question_type of the standard Sensei question types
814
     * @param string $answer
815
     * @param int $user_id
816
     *
817
     * @return int $question_grade
818
     */
819
    public static function grade_question_auto( $question_id = 0, $question_type = '', $answer = '', $user_id = 0 ) {
820
821
        if( intval( $user_id ) == 0 ) {
822
823
            $user_id = get_current_user_id();
0 ignored issues
show
$user_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...
824
825
        }
826
827
        if( ! ( intval( $question_id ) > 0 ) ) {
828
829
            return false;
830
831
        }
832
833
834
        Sensei()->question->get_question_type( $question_id );
835
836
        /**
837
         * Applying a grade before the auto grading takes place.
838
         *
839
         * This filter is applied just before the question is auto graded. It fires in the context of a single question
840
         * in the sensei_grade_question_auto function. It fires irrespective of the question type. If you return a value
841
         * other than false the auto grade functionality will be ignored and your supplied grade will be user for this question.
842
         *
843
         * @param int $question_grade default false
844
         * @param int $question_id
845
         * @param string $question_type one of the Sensei question type.
846
         * @param string $answer user supplied question answer
847
         */
848
        $question_grade = apply_filters( 'sensei_pre_grade_question_auto', false, $question_id, $question_type, $answer );
849
850
        if ( false !== $question_grade ) {
851
852
            return $question_grade;
853
854
        }
855
856
        // auto grading core
857
        if( in_array( $question_type ,  array( 'multiple-choice'  , 'boolean'  ) )   ){
858
859
            $right_answer = (array) get_post_meta( $question_id, '_question_right_answer', true );
860
861
            if( 0 == get_magic_quotes_gpc() ) {
862
                $answer = wp_unslash( $answer );
863
            }
864
            $answer = (array) $answer;
865
            if ( is_array( $right_answer ) && count( $right_answer ) == count( $answer ) ) {
866
                // Loop through all answers ensure none are 'missing'
867
                $all_correct = true;
868
                foreach ( $answer as $check_answer ) {
869
                    if ( !in_array( $check_answer, $right_answer ) ) {
870
                        $all_correct = false;
871
                    }
872
                }
873
                // If all correct then grade
874
                if ( $all_correct ) {
875
                    $question_grade = Sensei()->question->get_question_grade( $question_id );
876
                }
877
            }
878
879
        } elseif( 'gap-fill' == $question_type ){
880
881
            $question_grade = self::grade_gap_fill_question( $question_id ,$answer );
882
883
        } else{
884
885
            /**
886
             * Grading questions that are not auto gradable.
887
             *
888
             * This filter is applied the context of ta single question within the sensei_grade_question_auto function.
889
             * It fires for all other questions types. It does not apply to 'multiple-choice'  , 'boolean' and gap-fill.
890
             *
891
             * @param int $question_grade default zero
892
             * @param int $question_id
893
             * @param string $question_type one of the Sensei question type.
894
             * @param string $answer user supplied question answer
895
             */
896
            $question_grade = ( int ) apply_filters( 'sensei_grade_question_auto', $question_grade, $question_id, $question_type, $answer );
897
898
        } // end if $question_type
899
900
        return $question_grade;
901
    } // end grade_question_auto
902
903
    /**
904
     * Grading logic specifically for the gap fill questions
905
     *
906
     * @since 1.9.0
907
     * @param $question_id
908
     * @param $user_answer
909
     *
910
     * @return bool | int false or the grade given to the user answer
911
     */
912
    public static function grade_gap_fill_question( $question_id, $user_answer ){
913
914
        $right_answer = get_post_meta( $question_id, '_question_right_answer', true );
915
        $gapfill_array = explode( '||', $right_answer );
916
917
        if( 0 == get_magic_quotes_gpc() ) { // deprecated from PHP 5.4 but we still support PHP 5.2
918
            $user_answer = wp_unslash( $user_answer );
919
        }
920
921
        /**
922
         * case sensitive grading filter
923
         *
924
         * alter the value simply use this code in your plugin or the themes functions.php
925
         * add_filter( 'sensei_gap_fill_case_sensitive_grading','__return_true' );
926
         *
927
         * @param bool $do_case_sensitive_comparison default false.
928
         *
929
         * @since 1.9.0
930
         */
931
        $do_case_sensitive_comparison = apply_filters('sensei_gap_fill_case_sensitive_grading', false );
932
933
        if( $do_case_sensitive_comparison ){
934
935
            // Case Sensitive Check that the 'gap' is "exactly" equal to the given answer
936 View Code Duplication
            if ( trim(($gapfill_array[1])) == trim( $user_answer ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
937
938
                return Sensei()->question->get_question_grade( $question_id );
939
940
            } else if (@preg_match('/' . $gapfill_array[1] . '/i', null) !== FALSE) {
941
942
                if (preg_match('/' . $gapfill_array[1] . '/i', $user_answer)) {
943
944
                    return Sensei()->question->get_question_grade($question_id);
945
946
                }else{
947
948
                    return false;
949
950
                }
951
952
            }else{
953
954
                return false;
955
956
            }
957
958 View Code Duplication
        }else{
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
959
960
            // Case Sensitive Check that the 'gap' is "exactly" equal to the given answer
961
            if ( trim(strtolower($gapfill_array[1])) == trim(strtolower( $user_answer )) ) {
962
963
               return Sensei()->question->get_question_grade( $question_id );
964
965
            } else if (@preg_match('/' . $gapfill_array[1] . '/i', null) !== FALSE) {
966
967
                if (preg_match('/' . $gapfill_array[1] . '/i', $user_answer)) {
968
969
                    return  Sensei()->question->get_question_grade( $question_id );
970
971
                }else{
972
973
                    return false;
974
975
                }
976
977
            }else{
978
979
                return false;
980
981
            }
982
983
        }
984
985
    }
986
987
    /**
988
     * Counts the lessons that have been graded manually and automatically
989
     *
990
     * @since 1.9.0
991
     * @return int $number_of_graded_lessons
992
     */
993
    public static function get_graded_lessons_count(){
994
995
        global $wpdb;
996
997
        $comment_query_piece[ 'select']  = "SELECT   COUNT(*) AS total";
998
        $comment_query_piece[ 'from']    = " FROM {$wpdb->comments}  INNER JOIN {$wpdb->commentmeta}  ON ( {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id ) ";
999
        $comment_query_piece[ 'where']   = " WHERE {$wpdb->comments}.comment_type IN ('sensei_lesson_status') AND ( {$wpdb->commentmeta}.meta_key = 'grade')";
1000
        $comment_query_piece[ 'orderby'] = " ORDER BY {$wpdb->comments}.comment_date_gmt DESC ";
1001
1002
        $comment_query = $comment_query_piece['select'] . $comment_query_piece['from'] . $comment_query_piece['where'] . $comment_query_piece['orderby'];
1003
        $number_of_graded_lessons = intval( $wpdb->get_var( $comment_query, 0, 0 ) );
1004
1005
        return $number_of_graded_lessons;
1006
    }
1007
1008
    /**
1009
     * Add together all the graded lesson grades
1010
     *
1011
     * @since 1.9.0
1012
     * @return double $sum_of_all_grades
1013
     */
1014
    public static function get_graded_lessons_sum(){
1015
1016
        global $wpdb;
1017
1018
        $comment_query_piece[ 'select']  = "SELECT SUM({$wpdb->commentmeta}.meta_value) AS meta_sum";
1019
        $comment_query_piece[ 'from']    = " FROM {$wpdb->comments}  INNER JOIN {$wpdb->commentmeta}  ON ( {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id ) ";
1020
        $comment_query_piece[ 'where']   = " WHERE {$wpdb->comments}.comment_type IN ('sensei_lesson_status') AND ( {$wpdb->commentmeta}.meta_key = 'grade')";
1021
        $comment_query_piece[ 'orderby'] = " ORDER BY {$wpdb->comments}.comment_date_gmt DESC ";
1022
1023
        $comment_query = $comment_query_piece['select'] . $comment_query_piece['from'] . $comment_query_piece['where'] . $comment_query_piece['orderby'];
1024
        $sum_of_all_grades = intval( $wpdb->get_var( $comment_query, 0, 0 ) );
1025
1026
        return $sum_of_all_grades;
1027
1028
    }
1029
1030
    /**
1031
     * Get the sum of all grades for the given user.
1032
     *
1033
     * @since 1.9.0
1034
     * @param $user_id
1035
     * @return double
1036
     */
1037 View Code Duplication
    public static function get_user_graded_lessons_sum( $user_id ){
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
1038
        global $wpdb;
1039
1040
        $clean_user_id = esc_sql( $user_id);
1041
        $comment_query_piece[ 'select']  = "SELECT SUM({$wpdb->commentmeta}.meta_value) AS meta_sum";
1042
        $comment_query_piece[ 'from']    = " FROM {$wpdb->comments}  INNER JOIN {$wpdb->commentmeta}  ON ( {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id ) ";
1043
        $comment_query_piece[ 'where']   = " WHERE {$wpdb->comments}.comment_type IN ('sensei_lesson_status') AND ( {$wpdb->commentmeta}.meta_key = 'grade') AND {$wpdb->comments}.user_id = {$clean_user_id} ";
1044
        $comment_query_piece[ 'orderby'] = " ORDER BY {$wpdb->comments}.comment_date_gmt DESC ";
1045
1046
        $comment_query = $comment_query_piece['select'] . $comment_query_piece['from'] . $comment_query_piece['where'] . $comment_query_piece['orderby'];
1047
        $sum_of_all_grades = intval( $wpdb->get_var( $comment_query, 0, 0 ) );
1048
1049
        return $sum_of_all_grades;
1050
    }
1051
1052
    /**
1053
     * Get the sum of all user grades for the given lesson.
1054
     *
1055
     * @since 1.9.0
1056
     *
1057
     * @param int lesson_id
1058
     * @return double
1059
     */
1060 View Code Duplication
    public static function get_lessons_users_grades_sum( $lesson_id ){
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
1061
1062
        global $wpdb;
1063
1064
        $clean_lesson_id = esc_sql( $lesson_id);
1065
        $comment_query_piece[ 'select']  = "SELECT SUM({$wpdb->commentmeta}.meta_value) AS meta_sum";
1066
        $comment_query_piece[ 'from']    = " FROM {$wpdb->comments}  INNER JOIN {$wpdb->commentmeta}  ON ( {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id ) ";
1067
        $comment_query_piece[ 'where']   = " WHERE {$wpdb->comments}.comment_type IN ('sensei_lesson_status') AND ( {$wpdb->commentmeta}.meta_key = 'grade') AND {$wpdb->comments}.comment_post_ID = {$clean_lesson_id} ";
1068
        $comment_query_piece[ 'orderby'] = " ORDER BY {$wpdb->comments}.comment_date_gmt DESC ";
1069
1070
        $comment_query = $comment_query_piece['select'] . $comment_query_piece['from'] . $comment_query_piece['where'] . $comment_query_piece['orderby'];
1071
        $sum_of_all_grades = intval( $wpdb->get_var( $comment_query, 0, 0 ) );
1072
1073
        return $sum_of_all_grades;
1074
1075
    }//get_lessons_user_grades_sum
1076
1077
    /**
1078
     * Get the sum of all user grades for the given course.
1079
     *
1080
     * @since 1.9.0
1081
     *
1082
     * @param int $course_id
1083
     * @return double
1084
     */
1085 View Code Duplication
    public static function get_course_users_grades_sum( $course_id ){
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
1086
1087
        global $wpdb;
1088
1089
        $clean_course_id = esc_sql( $course_id);
1090
        $comment_query_piece[ 'select']  = "SELECT SUM({$wpdb->commentmeta}.meta_value) AS meta_sum";
1091
        $comment_query_piece[ 'from']    = " FROM {$wpdb->comments}  INNER JOIN {$wpdb->commentmeta}  ON ( {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id ) ";
1092
        $comment_query_piece[ 'where']   = " WHERE {$wpdb->comments}.comment_type IN ('sensei_course_status') AND ( {$wpdb->commentmeta}.meta_key = 'percent') AND {$wpdb->comments}.comment_post_ID = {$clean_course_id} ";
1093
        $comment_query_piece[ 'orderby'] = " ORDER BY {$wpdb->comments}.comment_date_gmt DESC ";
1094
1095
        $comment_query = $comment_query_piece['select'] . $comment_query_piece['from'] . $comment_query_piece['where'] . $comment_query_piece['orderby'];
1096
        $sum_of_all_grades = intval( $wpdb->get_var( $comment_query, 0, 0 ) );
1097
1098
        return $sum_of_all_grades;
1099
1100
    }//get_lessons_user_grades_sum
1101
1102
} // End Class
1103
1104
/**
1105
 * Class WooThemes_Sensei_Grading
1106
 * @ignore only for backward compatibility
1107
 * @since 1.9.0
1108
 */
1109
class WooThemes_Sensei_Grading extends Sensei_Grading{}
1110