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.

class-sensei-analysis-course-list-table.php (16 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 11 and the first side effect is on line 2.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4
/**
5
 * Admin Analysis Course Data Table in Sensei.
6
 *
7
 * @package Analytics
8
 * @author Automattic
9
 * @since 1.2.0
10
 */
11
class Sensei_Analysis_Course_List_Table extends WooThemes_Sensei_List_Table {
12
	public $user_id;
13
	public $course_id;
14
	public $total_lessons;
15
	public $user_ids;
16
	public $view = 'lesson';
17
	public $page_slug = 'sensei_analysis';
18
19
	/**
20
	 * Constructor
21
	 * @since  1.2.0
22
	 */
23
	public function __construct ( $course_id = 0, $user_id = 0 ) {
24
		$this->course_id = intval( $course_id );
25
		$this->user_id = intval( $user_id );
26
27
		if( isset( $_GET['view'] ) && in_array( $_GET['view'], array( 'user', 'lesson' ) ) ) {
28
			$this->view = $_GET['view'];
29
		}
30
31
		// Viewing a single Learner always sets the view to Lessons
32
		if( $this->user_id ) {
33
			$this->view = 'lesson';
34
		}
35
36
		// Load Parent token into constructor
37
		parent::__construct( 'analysis_course' );
38
39
		// Actions
40
		add_action( 'sensei_before_list_table', array( $this, 'data_table_header' ) );
41
		add_action( 'sensei_after_list_table', array( $this, 'data_table_footer' ) );
42
43
		add_filter( 'sensei_list_table_search_button_text', array( $this, 'search_button' ) );
44
45
	} // End __construct()
46
47
	/**
48
	 * Define the columns that are going to be used in the table
49
	 * @since  1.7.0
50
	 * @return array $columns, the array of columns to use with the table
51
	 */
52
	function get_columns() {
0 ignored issues
show
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...
53
54
		switch( $this->view ) {
55 View Code Duplication
			case 'user' :
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...
56
				$columns = array(
57
					'title' => __( 'Learner', 'woothemes-sensei' ),
58
					'started' => __( 'Date Started', 'woothemes-sensei' ),
59
					'completed' => __( 'Date Completed', 'woothemes-sensei' ),
60
					'user_status' => __( 'Status', 'woothemes-sensei' ),
61
					'percent' => __( 'Percent Complete', 'woothemes-sensei' ),
62
				);
63
				break;
64
65
			case 'lesson' :
66
			default:
67
				if ( $this->user_id ) {
68
69
					$columns = array(
70
						'title' => __( 'Lesson', 'woothemes-sensei' ),
71
						'started' => __( 'Date Started', 'woothemes-sensei' ),
72
						'completed' => __( 'Date Completed', 'woothemes-sensei' ),
73
						'user_status' => __( 'Status', 'woothemes-sensei' ),
74
						'grade' => __( 'Grade', 'woothemes-sensei' ),
75
					);
76
77
				} else {
78
79
					$columns = array(
80
						'title' => __( 'Lesson', 'woothemes-sensei' ),
81
						'num_learners' => __( 'Learners', 'woothemes-sensei' ),
82
						'completions' => __( 'Completed', 'woothemes-sensei' ),
83
						'average_grade' => __( 'Average Grade', 'woothemes-sensei' ),
84
					);
85
86
				}
87
				break;
88
		}
89
		// Backwards compatible
90
		$columns = apply_filters( 'sensei_analysis_course_' . $this->view . '_columns', $columns, $this );
91
		// Moving forward, single filter with args
92
		$columns = apply_filters( 'sensei_analysis_course_columns', $columns, $this );
93
		return $columns;
94
	}
95
96
	/**
97
	 * Define the columns that are going to be used in the table
98
	 * @since  1.7.0
99
	 * @return array $columns, the array of columns to use with the table
100
	 */
101
	function get_sortable_columns() {
0 ignored issues
show
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...
102
103
		switch( $this->view ) {
104
			case 'user' :
105
				$columns = array(
106
					'title' => array( 'title', false ),
107
					'started' => array( 'started', false ),
108
					'completed' => array( 'completed', false ),
109
					'user_status' => array( 'user_status', false ),
110
//					'grade' => array( 'grade', false ),
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...
111
					'percent' => array( 'percent', false )
112
				);
113
				break;
114
115
			case 'lesson' :
116
			default:
117
				if ( $this->user_id ) {
118
119
					$columns = array(
120
						'title' => array( 'title', false ),
121
						'started' => array( 'started', false ),
122
						'completed' => array( 'completed', false ),
123
						'user_status' => array( 'user_status', false ),
124
						'grade' => array( 'grade', false ),
125
					);
126
127
				} else {
128
129
					$columns = array(
130
						'title' => array( 'title', false ),
131
						'num_learners' => array( 'num_learners', false ),
132
						'completions' => array( 'completions', false ),
133
						'average_grade' => array( 'average_grade', false )
134
					);
135
136
				}
137
				break;
138
		}
139
		// Backwards compatible
140
		$columns = apply_filters( 'sensei_analysis_course_' . $this->view . '_columns_sortable', $columns, $this );
141
		// Moving forward, single filter with args
142
		$columns = apply_filters( 'sensei_analysis_course_columns_sortable', $columns, $this );
143
		return $columns;
144
	}
145
146
	/**
147
	 * Prepare the table with different parameters, pagination, columns and table elements
148
	 * @since  1.7.0
149
	 * @return void
150
	 */
151
	public function prepare_items() {
152
		global $per_page;
153
154
		// Handle orderby (needs work)
155
		$orderby = '';
156
		if ( !empty( $_GET['orderby'] ) ) {
157
			if ( array_key_exists( esc_html( $_GET['orderby'] ), $this->get_sortable_columns() ) ) {
158
				$orderby = esc_html( $_GET['orderby'] );
159
			} // End If Statement
160
		}
161
162
		// Handle order
163
		$order = 'ASC';
164
		if ( !empty( $_GET['order'] ) ) {
165
			$order = ( 'ASC' == strtoupper($_GET['order']) ) ? 'ASC' : 'DESC';
166
		}
167
168
		// Handle search, need 4.1 version of WP to be able to restrict statuses to known post_ids
169
		$search = false;
170
		if ( !empty( $_GET['s'] ) ) {
171
			$search = esc_html( $_GET['s'] );
172
		} // End If Statement
173
		$this->search = $search;
174
175
		$per_page = $this->get_items_per_page( 'sensei_comments_per_page' );
176
		$per_page = apply_filters( 'sensei_comments_per_page', $per_page, 'sensei_comments' );
177
178
		$paged = $this->get_pagenum();
179
		$offset = 0;
180
		if ( !empty($paged) ) {
181
			$offset = $per_page * ( $paged - 1 );
182
		} // End If Statement
183
184
		$args = array(
185
			'number' => $per_page,
186
			'offset' => $offset,
187
			'orderby' => $orderby,
188
			'order' => $order,
189
		);
190
		if ( $this->search ) {
191
			$args['search'] = $this->search;
192
		} // End If Statement
193
194 View Code Duplication
		switch( $this->view ) {
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...
195
			case 'user' :
196
				$this->items = $this->get_course_statuses( $args );
197
				break;
198
199
			case 'lesson':
200
			default:
201
				$this->items = $this->get_lessons( $args );
202
				break;
203
		}
204
205
		$total_items = $this->total_items;
206
		$total_pages = ceil( $total_items / $per_page );
207
		$this->set_pagination_args( array(
208
			'total_items' => $total_items,
209
			'total_pages' => $total_pages,
210
			'per_page' => $per_page
211
		) );
212
	}
213
214
	/**
215
	 * Generate a csv report with different parameters, pagination, columns and table elements
216
	 * @since  1.7.0
217
	 * @return data
218
	 */
219
	public function generate_report( $report ) {
220
221
		$data = array();
222
223
		$this->csv_output = true;
224
225
		// Handle orderby
226
		$orderby = '';
227
		if ( !empty( $_GET['orderby'] ) ) {
228
			if ( array_key_exists( esc_html( $_GET['orderby'] ), $this->get_sortable_columns() ) ) {
229
				$orderby = esc_html( $_GET['orderby'] );
230
			} // End If Statement
231
		}
232
233
		// Handle order
234
		$order = 'ASC';
235
		if ( !empty( $_GET['order'] ) ) {
236
			$order = ( 'ASC' == strtoupper($_GET['order']) ) ? 'ASC' : 'DESC';
237
		}
238
239
		// Handle search
240
		$search = false;
241
		if ( !empty( $_GET['s'] ) ) {
242
			$search = esc_html( $_GET['s'] );
243
		} // End If Statement
244
		$this->search = $search;
245
246
		$args = array(
247
			'orderby' => $orderby,
248
			'order' => $order,
249
		);
250
		if ( $this->search ) {
251
			$args['search'] = $this->search;
252
		} // End If Statement
253
254
		// Start the csv with the column headings
255
		$column_headers = array();
256
		$columns = $this->get_columns();
257
		foreach( $columns AS $key => $title ) {
258
			$column_headers[] = $title;
259
		}
260
		$data[] = $column_headers;
261
262 View Code Duplication
		switch( $this->view ) {
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...
263
			case 'user' :
264
				$this->items = $this->get_course_statuses( $args );
265
				break;
266
267
			case 'lesson':
268
			default:
269
				$this->items = $this->get_lessons( $args );
270
				break;
271
		}
272
273
		// Process each row
274
		foreach( $this->items AS $item) {
275
			$data[] = $this->get_row_data( $item );
276
		}
277
278
		return $data;
279
	}
280
281
	/**
282
	 * Generates the overall array for a single item in the display
283
	 *
284
	 * @since  1.7.0
285
	 * @param object $item The current item
286
	 */
287
	protected function get_row_data( $item ) {
288
289
		switch( $this->view ) {
290
			case 'user' :
291
				$user_start_date = get_comment_meta( $item->comment_ID, 'start', true );
292
				$user_end_date = $item->comment_date;
293
294 View Code Duplication
				if( 'complete' == $item->comment_approved ) {
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...
295
296
					$status =  __( 'Completed', 'woothemes-sensei' );
297
					$status_class = 'graded';
298
299
				} else {
300
301
					$status =  __( 'In Progress', 'woothemes-sensei' );
302
					$status_class = 'in-progress';
303
					$user_end_date = '';
304
305
				}
306
				$course_percent = get_comment_meta( $item->comment_ID, 'percent', true );
307
308
				// Output users data
309
				$user_name = Sensei_Learner::get_full_name( $item->user_id );
310
311 View Code Duplication
				if ( !$this->csv_output ) {
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...
312
313
					$url = add_query_arg( array( 'page' => $this->page_slug, 'user_id' => $item->user_id, 'course_id' => $this->course_id ), admin_url( 'admin.php' ) );
314
315
					$user_name = '<strong><a class="row-title" href="' . esc_url( $url ) . '">' . $user_name . '</a></strong>';
316
					$status = sprintf( '<span class="%s">%s</span>', $status_class, $status );
317
					if ( is_numeric($course_percent) ) {
318
319
						$course_percent .= '%';
320
321
					}
322
323
				} // End If Statement
324
325
				$column_data = apply_filters( 'sensei_analysis_course_column_data', array( 'title' => $user_name,
326
												'started' => $user_start_date,
327
												'completed' => $user_end_date,
328
												'user_status' => $status,
329
												'percent' => $course_percent,
330
											), $item, $this );
331
				break;
332
333
			case 'lesson':
334
			default:
335
				// Displaying lessons for this Course for a specific User
336
				if ( $this->user_id ) {
337
					$status = __( 'Not started', 'woothemes-sensei' );
338
					$user_start_date = $user_end_date = $status_class = $grade = '';
339
340
					$lesson_args = array(
341
							'post_id' => $item->ID,
342
							'user_id' => $this->user_id,
343
							'type' => 'sensei_lesson_status',
344
							'status' => 'any',
345
						);
346
					$lesson_status = Sensei_Utils::sensei_check_for_activity( apply_filters( 'sensei_analysis_course_user_lesson', $lesson_args, $item, $this->user_id ), true );
347
348
					if ( !empty($lesson_status) ) {
349
						$user_start_date = get_comment_meta( $lesson_status->comment_ID, 'start', true );
350
						$user_end_date = $lesson_status->comment_date;
351
352 View Code Duplication
						if( 'complete' == $lesson_status->comment_approved ) {
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...
353
							$status = __( 'Completed', 'woothemes-sensei' );
354
							$status_class = 'graded';
355
356
							$grade = __( 'No Grade', 'woothemes-sensei' );
357
						}
358
						elseif( 'graded' == $lesson_status->comment_approved ) {
359
							$status =  __( 'Graded', 'woothemes-sensei' );
360
							$status_class = 'graded';
361
362
							$grade = get_comment_meta( $lesson_status->comment_ID, 'grade', true);
363
						}
364
						elseif( 'passed' == $lesson_status->comment_approved ) {
365
							$status =  __( 'Passed', 'woothemes-sensei' );
366
							$status_class = 'graded';
367
368
							$grade = get_comment_meta( $lesson_status->comment_ID, 'grade', true);
369
						}
370
						elseif( 'failed' == $lesson_status->comment_approved ) {
371
							$status =  __( 'Failed', 'woothemes-sensei' );
372
							$status_class = 'failed';
373
374
							$grade = get_comment_meta( $lesson_status->comment_ID, 'grade', true);
375
						}
376
						elseif( 'ungraded' == $lesson_status->comment_approved ) {
377
							$status =  __( 'Ungraded', 'woothemes-sensei' );
378
							$status_class = 'ungraded';
379
380
						}
381
						elseif( 'in-progress' == $lesson_status->comment_approved ) {
382
							$status =  __( 'In Progress', 'woothemes-sensei' );
383
							$user_end_date = '';
384
						}
385
					} // END lesson_status
386
387
					// Output users data
388
					if ( $this->csv_output ) {
389
						$lesson_title = apply_filters( 'the_title', $item->post_title, $item->ID );
390
					}
391
					else {
392
						$url = add_query_arg( array( 'page' => $this->page_slug, 'lesson_id' => $item->ID ), admin_url( 'admin.php' ) );
393
						$lesson_title = '<strong><a class="row-title" href="' . esc_url( $url ) . '">' . apply_filters( 'the_title', $item->post_title, $item->ID ) . '</a></strong>';
394
395
						$status = sprintf( '<span class="%s">%s</span>', $status_class, $status );
396
						if ( is_numeric($grade) ) {
397
							$grade .= '%';
398
						}
399
					} // End If Statement
400
					$column_data = apply_filters( 'sensei_analysis_course_column_data', array( 'title' => $lesson_title,
401
													'started' => $user_start_date,
402
													'completed' => $user_end_date,
403
													'user_status' => $status,
404
													'grade' => $grade,
405
												), $item, $this );
406
				}
407
				// Display lessons for this Course regardless of users
408
				else {
409
					// Get Learners (i.e. those who have started)
410
					$lesson_args = array(
411
							'post_id' => $item->ID,
412
							'type' => 'sensei_lesson_status',
413
							'status' => 'any',
414
						);
415
					$lesson_students = Sensei_Utils::sensei_check_for_activity( apply_filters( 'sensei_analysis_lesson_learners', $lesson_args, $item ) );
416
417
					// Get Course Completions
418
					$lesson_args = array(
419
							'post_id' => $item->ID,
420
							'type' => 'sensei_lesson_status',
421
							'status' => array( 'complete', 'graded', 'passed', 'failed' ),
422
							'count' => true,
423
						);
424
					$lesson_completions = Sensei_Utils::sensei_check_for_activity( apply_filters( 'sensei_analysis_lesson_completions', $lesson_args, $item ) );
425
426
					$lesson_average_grade = __('n/a', 'woothemes-sensei');
427
					if ( false != get_post_meta($item->ID, '_quiz_has_questions', true) ) {
428
						// Get Percent Complete
429
						$grade_args = array(
430
								'post_id' => $item->ID,
431
								'type' => 'sensei_lesson_status',
432
								'status' => array( 'graded', 'passed', 'failed' ),
433
								'meta_key' => 'grade',
434
							);
435
						add_filter( 'comments_clauses', array( 'WooThemes_Sensei_Utils', 'comment_total_sum_meta_value_filter' ) );
436
						$lesson_grades = Sensei_Utils::sensei_check_for_activity( apply_filters( 'sensei_analysis_lesson_grades', $grade_args, $item ), true );
437
						remove_filter( 'comments_clauses', array( 'WooThemes_Sensei_Utils', 'comment_total_sum_meta_value_filter' ) );
438
439
						$grade_count = !empty( $lesson_grades->total ) ? $lesson_grades->total : 1;
440
						$grade_total = !empty( $lesson_grades->meta_sum ) ? doubleval( $lesson_grades->meta_sum ) : 0;
441
						$lesson_average_grade = abs( round( doubleval( $grade_total / $grade_count ), 2 ) );
442
					}
443
					// Output lesson data
444 View Code Duplication
					if ( $this->csv_output ) {
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...
445
						$lesson_title = apply_filters( 'the_title', $item->post_title, $item->ID );
446
					}
447
					else {
448
						$url = add_query_arg( array( 'page' => $this->page_slug, 'lesson_id' => $item->ID ), admin_url( 'admin.php' ) );
449
						$lesson_title = '<strong><a class="row-title" href="' . esc_url( $url ) . '">' . apply_filters( 'the_title', $item->post_title, $item->ID ) . '</a></strong>';
450
451
						if ( is_numeric( $lesson_average_grade ) ) {
452
							$lesson_average_grade .= '%';
453
						}
454
					} // End If Statement
455
					$column_data = apply_filters( 'sensei_analysis_course_column_data', array( 'title' => $lesson_title,
456
													'num_learners' => $lesson_students,
457
													'completions' => $lesson_completions,
458
													'average_grade' => $lesson_average_grade,
459
												), $item, $this );
460
				} // END if
461
				break;
462
		} // END switch
463
464
		return $column_data;
465
	}
466
467
	/**
468
	 * Return array of course statuses
469
	 * @since  1.7.0
470
	 * @return array statuses
471
	 */
472 View Code Duplication
	private function get_course_statuses( $args ) {
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...
473
474
		$activity_args = array(
475
				'post_id' => $this->course_id,
476
				'type' => 'sensei_course_status',
477
				'number' => $args['number'],
478
				'offset' => $args['offset'],
479
				'orderby' => $args['orderby'],
480
				'order' => $args['order'],
481
				'status' => 'any',
482
			);
483
484
		// Searching users on statuses requires sub-selecting the statuses by user_ids
485
		if ( $this->search ) {
486
			$user_args = array(
487
				'search' => '*' . $this->search . '*',
488
				'fields' => 'ID',
489
			);
490
			// Filter for extending
491
			$user_args = apply_filters( 'sensei_analysis_course_search_users', $user_args );
492
			if ( !empty( $user_args ) ) {
493
				$learners_search = new WP_User_Query( $user_args );
494
				// Store for reuse on counts
495
				$activity_args['user_id'] = (array) $learners_search->get_results();
496
			}
497
		} // End If Statement
498
499
		$activity_args = apply_filters( 'sensei_analysis_course_filter_statuses', $activity_args );
500
501
		// WP_Comment_Query doesn't support SQL_CALC_FOUND_ROWS, so instead do this twice
502
		$this->total_items = Sensei_Utils::sensei_check_for_activity( array_merge( $activity_args, array('count' => true, 'offset' => 0, 'number' => 0) ) );
503
504
		// Ensure we change our range to fit (in case a search threw off the pagination) - Should this be added to all views?
505
		if ( $this->total_items < $activity_args['offset'] ) {
506
			$new_paged = floor( $this->total_items / $activity_args['number'] );
507
			$activity_args['offset'] = $new_paged * $activity_args['number'];
508
		}
509
		$statuses = Sensei_Utils::sensei_check_for_activity( $activity_args, true );
510
		// Need to always return an array, even with only 1 item
511
		if ( !is_array($statuses) ) {
512
			$statuses = array( $statuses );
513
		}
514
		return $statuses;
515
	} // End get_course_statuses()
516
517
	/**
518
	 * Return array of Courses' lessons
519
	 * @since  1.7.0
520
	 * @return array statuses
521
	 */
522
	private function get_lessons( $args ) {
523
524
		$lessons_args = array( 'post_type'         => 'lesson',
525
							'posts_per_page'      => $args['number'],
526
							'offset'              => $args['offset'],
527
							'meta_key'            => '_order_' . $this->course_id,
528
//							'orderby'             => $args['orderby'],
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
529
							'order'               => $args['order'],
530
							'meta_query'          => array(
531
								array(
532
									'key' => '_lesson_course',
533
									'value' => intval( $this->course_id ),
534
								),
535
							),
536
							'post_status'         => array('publish', 'private'),
537
							'suppress_filters'    => 0
538
							);
539
		if ( $this->search ) {
540
			$lessons_args['s'] = $this->search;
541
		}
542
		if ( $this->csv_output ) {
543
			$lessons_args['posts_per_page'] = '-1';
544
		}
545
546
		// Using WP_Query as get_posts() doesn't support 'found_posts'
547
		$lessons_query = new WP_Query( apply_filters( 'sensei_analysis_course_filter_lessons', $lessons_args ) );
548
		$this->total_items = $lessons_query->found_posts;
549
		return $lessons_query->posts;
550
	} // End get_lessons()
551
552
	/**
553
	 * Sets output when no items are found
554
	 * Overloads the parent method
555
	 * @since  1.2.0
556
	 * @return void
557
	 */
558 View Code Duplication
	public function no_items() {
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...
559
		switch( $this->view ) {
560
			case 'user' :
561
				$text = __( 'No learners found.', 'woothemes-sensei' );
562
				break;
563
564
			case 'lesson':
565
			default:
566
				$text = __( 'No lessons found.', 'woothemes-sensei' );
567
				break;
568
		}
569
		echo apply_filters( 'sensei_analysis_course_no_items_text', $text );
570
	} // End no_items()
571
572
	/**
573
	 * Output for table heading
574
	 * @since  1.2.0
575
	 * @return void
576
	 */
577
	public function data_table_header() {
578
		if ( $this->user_id ) {
579
			$learners_text = __( 'Other Learners taking this Course', 'woothemes-sensei' );
580
		}
581
		else {
582
			$learners_text = __( 'Learners taking this Course', 'woothemes-sensei' );
583
		}
584
		$lessons_text = __( 'Lessons in this Course', 'woothemes-sensei' );
585
586
		$url_args = array(
587
			'page' => $this->page_slug,
588
			'course_id' => $this->course_id,
589
		);
590
		$learners_url = esc_url( add_query_arg( array_merge( $url_args, array( 'view' => 'user' ) ), admin_url( 'admin.php' ) ) );
591
		$lessons_url = esc_url( add_query_arg( array_merge( $url_args, array( 'view' => 'lesson' ) ), admin_url( 'admin.php' ) ) );
592
593
		$learners_class = $lessons_class = '';
594
595
		$menu = array();
596
		switch( $this->view ) {
597
			case 'user' :
598
				$learners_class = 'current';
599
				break;
600
601
			case 'lesson':
602
			default:
603
				$lessons_class = 'current';
604
				break;
605
		}
606
		$menu['lesson'] = sprintf( '<a href="%s" class="%s">%s</a>', $lessons_url, $lessons_class, $lessons_text );
607
		$menu['user'] = sprintf( '<a href="%s" class="%s">%s</a>', $learners_url, $learners_class, $learners_text );
608
609
		$menu = apply_filters( 'sensei_analysis_course_sub_menu', $menu );
610 View Code Duplication
		if ( !empty($menu) ) {
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...
611
			echo '<ul class="subsubsub">' . "\n";
612
			foreach ( $menu as $class => $item ) {
613
				$menu[ $class ] = "\t<li class='$class'>$item";
614
			}
615
			echo implode( " |</li>\n", $menu ) . "</li>\n";
616
			echo '</ul>' . "\n";
617
		}
618
	} // End data_table_header()
619
620
	/**
621
	 * Output for table footer
622
	 * @since  1.2.0
623
	 * @return void
624
	 */
625
	public function data_table_footer() {
626
627
		$course = get_post( $this->course_id );
628
		$report = sanitize_title( $course->post_title ) . '-' . $this->view . 's-overview';
629
		if ( $this->user_id ) {
630
            $user_name = Sensei_Learner::get_full_name( $this->user_id );
631
			$report = sanitize_title( $user_name  ) . '-' . $report;
632
		}
633
634
		$url_args = array( 'page' => $this->page_slug, 'course_id' => $this->course_id, 'view' => $this->view, 'sensei_report_download' => $report );
635
		if ( $this->user_id ) {
636
			$url_args['user_id'] = $this->user_id;
637
		}
638
		$url =  add_query_arg( $url_args, admin_url( 'admin.php' ) );
639
		echo '<a class="button button-primary" href="' . esc_url( wp_nonce_url( $url, 'sensei_csv_download-' . $report, '_sdl_nonce' ) ) . '">' . __( 'Export all rows (CSV)', 'woothemes-sensei' ) . '</a>';
640
	} // End data_table_footer()
641
642
	/**
643
	 * The text for the search button
644
	 * @since  1.7.0
645
	 * @return string $text
646
	 */
647 View Code Duplication
	public function search_button( $text = '' ) {
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...
648
		switch( $this->view ) {
649
			case 'user':
650
				$text = __( 'Search Learners', 'woothemes-sensei' );
651
			break;
652
653
			case 'lesson':
654
			default:
655
				$text = __( 'Search Lessons', 'woothemes-sensei' );
656
			break;
657
		} // End Switch Statement
658
659
		return $text;
660
	}
661
662
} // End Class
663
664
/**
665
 * Class WooThemes_Sensei_Analysis_Course_List_Table
666
 * @ignore only for backward compatibility
667
 * @since 1.9.0
668
 * @ignore
669
 */
670
class WooThemes_Sensei_Analysis_Course_List_Table extends Sensei_Analysis_Course_List_Table {}
671