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-analysis.php (14 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
 * All functionality pertaining to the Admin Analysis in Sensei.
6
 *
7
 * @package Analytics
8
 * @author Automattic
9
 * @since 1.0.0
10
 */
11
class Sensei_Analysis {
12
13
	public $name;
14
	public $file;
15
	public $page_slug;
16
17
	/**
18
	 * Constructor
19
	 * @since  1.0.0
20
	 * @param string $file
21
	 */
22
	public function __construct ( $file ) {
23
		$this->name = __('Analysis', 'woothemes-sensei');
24
		$this->file = $file;
25
		$this->page_slug = 'sensei_analysis';
26
27
		// Admin functions
28
		if ( is_admin() ) {
29
			add_action( 'admin_menu', array( $this, 'analysis_admin_menu' ), 10);
30
			add_action( 'analysis_wrapper_container', array( $this, 'wrapper_container'  ) );
31
			if ( isset( $_GET['page'] ) && ( $_GET['page'] == $this->page_slug ) ) {
32
33
				add_action( 'admin_print_styles', array( $this, 'enqueue_styles' ) );
34
35
			}
36
37
			add_action( 'admin_init', array( $this, 'report_download_page' ) );
38
39
			add_filter( 'user_search_columns', array( $this, 'user_search_columns_filter' ), 10, 3 );
40
		} // End If Statement
41
	} // End __construct()
42
43
44
	/**
45
	 * analysis_admin_menu function.
46
	 * @since  1.0.0
47
	 * @access public
48
	 * @return void
49
	 */
50 View Code Duplication
	public function analysis_admin_menu() {
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...
51
		global $menu, $woocommerce;
52
53
		if ( current_user_can( 'manage_sensei_grades' ) ) {
54
55
            add_submenu_page( 'sensei', __('Analysis', 'woothemes-sensei'),  __('Analysis', 'woothemes-sensei') , 'manage_sensei_grades', 'sensei_analysis', array( $this, 'analysis_page' ) );
56
57
		}
58
59
	} // End analysis_admin_menu()
60
61
	/**
62
	 * enqueue_styles function.
63
	 *
64
	 * @description Load in CSS styles where necessary.
65
	 * @access public
66
	 * @since 1.0.0
67
	 * @return void
68
	 */
69
	public function enqueue_styles () {
70
71
		wp_enqueue_style( 'woothemes-sensei-admin' );
72
73
		wp_enqueue_style( 'woothemes-sensei-settings-api', Sensei()->plugin_url . 'assets/css/settings.css', '', Sensei()->version );
74
75
	} // End enqueue_styles()
76
77
	/**
78
	 * load_data_table_files loads required files for Analysis
79
	 * @since  1.2.0
80
	 * @return void
81
	 */
82
	public function load_data_table_files() {
83
84
		// Load Analysis Classes
85
		$classes_to_load = array(	'list-table',
86
									'analysis-overview',
87
									'analysis-user-profile',
88
									'analysis-course',
89
									'analysis-lesson'
90
									);
91
		foreach ( $classes_to_load as $class_file ) {
92
            Sensei()->load_class( $class_file );
93
		} // End For Loop
94
	} // End load_data_table_files()
95
96
	/**
97
	 * load_data_object creates new instance of class
98
	 * @param  string  $name          Name of class
99
	 * @param  integer $data          constructor arguments
100
	 * @param  undefined  $optional_data optional constructor arguments
101
	 * @return object                 class instance object
102
	 */
103
	public function load_data_object( $name = '', $data = 0, $optional_data = null ) {
104
		// Load Analysis data
105
		$object_name = 'Sensei_Analysis_' . $name . '_List_Table';
106
		if ( is_null($optional_data) ) {
107
			$sensei_analysis_object = new $object_name( $data );
108
		} else {
109
			$sensei_analysis_object = new $object_name( $data, $optional_data );
110
		}
111
		$sensei_analysis_object->prepare_items();
112
		return $sensei_analysis_object;
113
	} // End load_data_object()
114
115
	/**
116
	 * analysis_page function.
117
	 * @since 1.0.0
118
	 * @access public
119
	 * @return void
120
	 */
121
	public function analysis_page() {
122
123
		$course_id = 0;
124
		$lesson_id = 0;
125
		$user_id = 0;
126
		if( isset( $_GET['course_id'] ) ) {
127
			$course_id = intval( $_GET['course_id'] );
128
		}
129
		if( isset( $_GET['lesson_id'] ) ) {
130
			$lesson_id = intval( $_GET['lesson_id'] );
131
		}
132
		if( isset( $_GET['user_id'] ) ) {
133
			$user_id = intval( $_GET['user_id'] );
134
		}
135
		$type = isset( $_GET['view'] ) ? esc_html( $_GET['view'] ) : false;
136
137
		if ( 0 < $lesson_id ) {
138
			// Viewing a specific Lesson and all its Learners
139
			$this->analysis_lesson_users_view( $lesson_id );
140
		}
141
		elseif ( 0 < $course_id && !$user_id && 'user' == $type ) {
142
			// Viewing a specific Course and all its Learners
143
			$this->analysis_course_users_view( $course_id );
144
		}
145
		elseif ( 0 < $course_id && 0 < $user_id ) {
146
			// Viewing a specific Learner on a specific Course, showing their Lessons
147
			$this->analysis_user_course_view( $course_id, $user_id );
148
		}
149
		elseif( 0 < $course_id ) {
150
			// Viewing a specific Course and all it's Lessons
151
			$this->analysis_course_view( $course_id );
152
		}
153
		elseif ( 0 < $user_id ) {
154
			// Viewing a specific Learner, and their Courses
155
			$this->analysis_user_profile_view( $user_id );
156
		}
157
		else {
158
			// Overview of all Learners, all Courses, or all Lessons
159
			$this->analysis_default_view( $type );
160
		} // End If Statement
161
	} // End analysis_page()
162
163
	/**
164
	 * Default view for analysis, showing an overview of all Learners, Courses and Lessons
165
	 * @since  1.2.0
166
	 * @return void
167
	 */
168
	public function analysis_default_view( $type ) {
169
170
		// Load Analysis data
171
		$sensei_analysis_overview = $this->load_data_object( 'Overview', $type );
172
		// Wrappers
173
		do_action( 'analysis_before_container' );
174
		do_action( 'analysis_wrapper_container', 'top' );
175
		$this->analysis_headers();
176
		?>
177
		<div id="poststuff" class="sensei-analysis-wrap">
178
			<div class="sensei-analysis-sidebar">
179
				<?php
180
				do_action( 'sensei_analysis_before_stats_boxes' );
181
				foreach ( $sensei_analysis_overview->stats_boxes() as $key => $value ) {
182
					$this->render_stats_box( esc_html( $key ), esc_html( $value ) );
183
				} // End For Loop
184
				do_action( 'sensei_analysis_after_stats_boxes' );
185
				?>
186
			</div>
187
			<div class="sensei-analysis-main">
188
				<?php $sensei_analysis_overview->display(); ?>
189
			</div>
190
			<div class="sensei-analysis-extra">
191
				<?php do_action( 'sensei_analysis_extra' ); ?>
192
			</div>
193
		</div>
194
		<?php
195
		do_action( 'analysis_wrapper_container', 'bottom' );
196
		do_action( 'analysis_after_container' );
197
	} // End analysis_default_view()
198
199
	/**
200
	 * An individual users' profile view for analysis, showing their Courses
201
	 * @since  1.2.0
202
	 * @param integer $user_id
203
	 * @return void
204
	 */
205 View Code Duplication
	public function analysis_user_profile_view( $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...
206
207
		// Load Analysis data
208
		$sensei_analysis_user_profile = $this->load_data_object( 'User_Profile', $user_id );
209
		// Wrappers
210
		do_action( 'analysis_before_container' );
211
		do_action( 'analysis_wrapper_container', 'top' );
212
		$this->analysis_headers( array( 'nav' => 'user_profile' ) );
213
		?>
214
		<div id="poststuff" class="sensei-analysis-wrap user-profile">
215
			<div class="sensei-analysis-main">
216
				<?php $sensei_analysis_user_profile->display(); ?>
217
			</div>
218
			<div class="sensei-analysis-extra">
219
				<?php do_action( 'sensei_analysis_extra' ); ?>
220
			</div>
221
		</div>
222
		<?php
223
		do_action( 'analysis_wrapper_container', 'bottom' );
224
		do_action( 'analysis_after_container' );
225
	} // End analysis_user_profile_view()
226
227
	/**
228
	 * An individual Course view for analysis, showing the Courses Lessons
229
	 * @since  1.2.0
230
	 * @param integer $course_id
231
	 * @return void
232
	 */
233 View Code Duplication
	public function analysis_course_view( $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...
234
235
		// Load Analysis data
236
		$sensei_analysis_course = $this->load_data_object( 'Course', $course_id );
237
		// Wrappers
238
		do_action( 'analysis_before_container' );
239
		do_action( 'analysis_wrapper_container', 'top' );
240
		$this->analysis_headers( array( 'nav' => 'course' ) );
241
		?>
242
		<div id="poststuff" class="sensei-analysis-wrap course-profile">
243
			<div class="sensei-analysis-main">
244
				<?php $sensei_analysis_course->display(); ?>
245
			</div>
246
			<div class="sensei-analysis-extra">
247
				<?php do_action( 'sensei_analysis_extra' ); ?>
248
			</div>
249
		</div>
250
		<?php
251
		do_action( 'analysis_wrapper_container', 'bottom' );
252
		do_action( 'analysis_after_container' );
253
	} // End analysis_course_view()
254
255
	/**
256
	 * An individual Course view for analysis, showing a specific Learners Lessons
257
	 * @since  1.2.0
258
	 * @param integer $course_id
259
	 * @param integer $user_id
260
	 * @return void
261
	 */
262 View Code Duplication
	public function analysis_user_course_view( $course_id, $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...
263
264
		// Load Analysis data
265
		$sensei_analysis_user_course = $this->load_data_object( 'Course', $course_id, $user_id );
0 ignored issues
show
$user_id is of type integer, but the function expects a object<undefined>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
266
		// Wrappers
267
		do_action( 'analysis_before_container' );
268
		do_action( 'analysis_wrapper_container', 'top' );
269
		$this->analysis_headers( array( 'nav' => 'user_course' ) );
270
		?>
271
		<div id="poststuff" class="sensei-analysis-wrap course-profile">
272
			<div class="sensei-analysis-main">
273
				<?php $sensei_analysis_user_course->display(); ?>
274
			</div>
275
			<div class="sensei-analysis-extra">
276
				<?php do_action( 'sensei_analysis_extra' ); ?>
277
			</div>
278
		</div>
279
		<?php
280
		do_action( 'analysis_wrapper_container', 'bottom' );
281
		do_action( 'analysis_after_container' );
282
	} // End analysis_user_course_view()
283
284
	/**
285
	 * An individual Course view for analysis, showing all the Learners
286
	 * @since  1.2.0
287
	 * @param integer $course_id
288
	 * @return void
289
	 */
290 View Code Duplication
	public function analysis_course_users_view( $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...
291
292
		// Load Analysis data
293
		$sensei_analysis_course_users = $this->load_data_object( 'Course', $course_id );
294
		// Wrappers
295
		do_action( 'analysis_before_container' );
296
		do_action( 'analysis_wrapper_container', 'top' );
297
		$this->analysis_headers( array( 'nav' => 'course_users' ) );
298
		?>
299
		<div id="poststuff" class="sensei-analysis-wrap course-profile">
300
			<div class="sensei-analysis-main">
301
				<?php $sensei_analysis_course_users->display(); ?>
302
			</div>
303
			<div class="sensei-analysis-extra">
304
				<?php do_action( 'sensei_analysis_extra' ); ?>
305
			</div>
306
		</div>
307
		<?php
308
		do_action( 'analysis_wrapper_container', 'bottom' );
309
		do_action( 'analysis_after_container' );
310
	} // End analysis_course_users_view()
311
312
	/**
313
	 * An individual Lesson view for analysis, showing all the Learners
314
	 * @since  1.2.0
315
	 * @param integer $lesson_id
316
	 * @return void
317
	 */
318 View Code Duplication
	public function analysis_lesson_users_view( $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...
319
320
		// Load Analysis data
321
		$sensei_analysis_lesson_users = $this->load_data_object( 'Lesson', $lesson_id );
322
		// Wrappers
323
		do_action( 'analysis_before_container' );
324
		do_action( 'analysis_wrapper_container', 'top' );
325
		$this->analysis_headers( array( 'nav' => 'lesson_users' ) );
326
		?>
327
		<div id="poststuff" class="sensei-analysis-wrap course-profile">
328
			<div class="sensei-analysis-main">
329
				<?php $sensei_analysis_lesson_users->display(); ?>
330
			</div>
331
			<div class="sensei-analysis-extra">
332
				<?php do_action( 'sensei_analysis_extra' ); ?>
333
			</div>
334
		</div>
335
		<?php
336
		do_action( 'analysis_wrapper_container', 'bottom' );
337
		do_action( 'analysis_after_container' );
338
	} // End analysis_lesson_users_view()
339
340
	/**
341
	 * render_stats_box outputs stats boxes
342
	 * @since  1.2.0
343
	 * @param  $title string title of stat
344
	 * @param  $data string stats data
345
	 * @return void
346
	 */
347
	public function render_stats_box( $title, $data ) {
348
		?><div class="postbox">
349
			<h2><span><?php echo $title; ?></span></h2>
350
			<div class="inside">
351
				<p class="stat"><?php echo $data; ?></p>
352
			</div>
353
		</div><?php
354
	} // End render_stats_box()
355
356
	/**
357
	 * analysis_headers outputs analysis general headers
358
	 * @since  1.2.0
359
	 * @return void
360
	 */
361 View Code Duplication
	public function analysis_headers( $args = array( 'nav' => 'default' ) ) {
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...
362
363
		$function = 'analysis_' . $args['nav'] . '_nav';
364
		$this->$function();
365
		?>
366
			<p class="powered-by-woo">
367
368
                <?php _e( 'Powered by', 'woothemes-sensei' ); ?>
369
370
                <a href="http://www.woothemes.com/" title="WooThemes">
371
372
                    <img src="<?php echo Sensei()->plugin_url; ?>assets/images/woothemes.png" alt="WooThemes" />
373
374
                </a>
375
376
            </p>
377
378
		<?php
379
		do_action( 'sensei_analysis_after_headers' );
380
	} // End analysis_headers()
381
382
	/**
383
	 * wrapper_container wrapper for analysis area
384
	 * @since  1.2.0
385
	 * @param $which string
386
	 * @return void
387
	 */
388
	public function wrapper_container( $which ) {
389
		if ( 'top' == $which ) {
390
			?><div id="woothemes-sensei" class="wrap woothemes-sensei"><?php
391
		} elseif ( 'bottom' == $which ) {
392
			?></div><!--/#woothemes-sensei--><?php
393
		} // End If Statement
394
	} // End wrapper_container()
395
396
	/**
397
	 * Default nav area for Analysis, overview of Learners, Courses and Lessons
398
	 * @since  1.2.0
399
	 * @return void
400
	 */
401
	public function analysis_default_nav() {
402
403
		$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 ) );
404
		$view = isset($_GET['view']) ? esc_html( $_GET['view'] ) : '';
405
		switch ( $view ) { 
406
			case 'courses' :
407
				$title .= sprintf( '&nbsp;&nbsp;<span class="course-title">&gt;&nbsp;&nbsp;%s</span>', __( 'Courses', 'woothemes-sensei' ) );
408
				break;
409
410
			case 'lessons' :
411
				$title .= sprintf( '&nbsp;&nbsp;<span class="lesson-title">&gt;&nbsp;&nbsp;%s</span>', __( 'Lessons', 'woothemes-sensei' ) );
412
				break;
413
414
			case 'users' :
415
			default :
416
				$title .= sprintf( '&nbsp;&nbsp;<span class="user-title">&gt;&nbsp;&nbsp;%s</span>', __( 'Learners', 'woothemes-sensei' ) );
417
				break;
418
		}
419
		?>
420
			<h1><?php echo apply_filters( 'sensei_analysis_nav_title', $title ); ?></h1>
421
		<?php
422
	} // End analysis_default_nav()
423
424
	/**
425
	 * Nav area for Analysis of a specific User profile
426
	 * @since  1.2.0
427
	 * @return void
428
	 */
429
	public function analysis_user_profile_nav() {
430
431
		$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 ) );
432
		if ( isset( $_GET['user_id'] ) && 0 < intval( $_GET['user_id'] ) ) {
433
434
			$user_id = intval( $_GET['user_id'] );
435
			$url = esc_url( add_query_arg( array( 'page' => $this->page_slug, 'user' => $user_id ), admin_url( 'admin.php' ) ) );
436
            $user_name = Sensei_Learner::get_full_name( $user_id );
437
			$title .= sprintf( '&nbsp;&nbsp;<span class="user-title">&gt;&nbsp;&nbsp;<a href="%s">%s</a></span>', $url, $user_name );
438
439
		} // End If Statement
440
		?>
441
			<h1><?php echo apply_filters( 'sensei_analysis_nav_title', $title ); ?></h1>
442
		<?php
443
	} // End analysis_user_profile_nav()
444
445
	/**
446
	 * Nav area for Analysis of a specific Course and its Lessons, specific to a User
447
	 * @since  1.2.0
448
	 * @return void
449
	 */
450
	public function analysis_user_course_nav() {
451
452
		$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 ) );
453
		if ( isset( $_GET['user_id'] ) && 0 < intval( $_GET['user_id'] ) ) {
454
			$user_id = intval( $_GET['user_id'] );
455
			$user_data = get_userdata( $user_id );
456
			$url = add_query_arg( array( 'page' => $this->page_slug, 'user_id' => $user_id ), admin_url( 'admin.php' ) );
457
            $user_name = Sensei_Learner::get_full_name( $user_id );
458
            $title .= sprintf( '&nbsp;&nbsp;<span class="user-title">&gt;&nbsp;&nbsp;<a href="%s">%s</a></span>', $url, $user_name );
459
			$title .= sprintf( '&nbsp;&nbsp;<span class="user-title">&gt;&nbsp;&nbsp;<a href="%s">%s</a></span>', esc_url( $url ), $user_data->display_name );
460
		} // End If Statement
461
		if ( isset( $_GET['course_id'] ) ) { 
462
			$course_id = intval( $_GET['course_id'] );
463
			$url = add_query_arg( array( 'page' => $this->page_slug, 'course_id' => $course_id ), admin_url( 'admin.php' ) );
464
			$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 ) );
465
		}
466
		?>
467
			<h1><?php echo apply_filters( 'sensei_analysis_nav_title', $title ); ?></h1>
468
		<?php
469
	} // End analysis_user_course_nav()
470
471
	/**
472
	 * Nav area for Analysis of a specific Course and displaying its Lessons
473
	 * @since  1.2.0
474
	 * @return void
475
	 */
476 View Code Duplication
	public function analysis_course_nav() {
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...
477
478
		$title = sprintf( '<a href="%s">%s</a>', add_query_arg( array( 'page' => $this->page_slug ), admin_url( 'admin.php' ) ), esc_html( $this->name ) );
479
		if ( isset( $_GET['course_id'] ) ) { 
480
			$course_id = intval( $_GET['course_id'] );
481
			$url = add_query_arg( array( 'page' => $this->page_slug, 'course_id' => $course_id ), admin_url( 'admin.php' ) );
482
			$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 ) );
483
		}
484
		?>
485
			<h1><?php echo apply_filters( 'sensei_analysis_nav_title', $title ); ?></h1>
486
		<?php
487
	} // End analysis_course_nav()
488
489
	/**
490
	 * Nav area for Analysis of a specific Course displaying its Users
491
	 * @since  1.2.0
492
	 * @return void
493
	 */
494 View Code Duplication
	public function analysis_course_users_nav() {
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...
495
496
		$title = sprintf( '<a href="%s">%s</a>', add_query_arg( array( 'page' => $this->page_slug ), admin_url( 'admin.php' ) ), esc_html( $this->name ) );
497
		if ( isset( $_GET['course_id'] ) ) { 
498
			$course_id = intval( $_GET['course_id'] );
499
			$url = add_query_arg( array( 'page' => $this->page_slug, 'course_id' => $course_id ), admin_url( 'admin.php' ) );
500
			$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 ) );
501
		}
502
		?>
503
			<h1><?php echo apply_filters( 'sensei_analysis_nav_title', $title ); ?></h1>
504
		<?php
505
	} // End analysis_course_users_nav()
506
507
	/**
508
	 * Nav area for Analysis of a specific Lesson displaying its Users
509
	 * @since  1.2.0
510
	 * @return void
511
	 */
512
	public function analysis_lesson_users_nav() {
513
514
		$title = sprintf( '<a href="%s">%s</a>', add_query_arg( array( 'page' => $this->page_slug ), admin_url( 'admin.php' ) ), esc_html( $this->name ) );
515
		if ( isset( $_GET['lesson_id'] ) ) { 
516
			$lesson_id = intval( $_GET['lesson_id'] );
517
			$course_id = intval( get_post_meta( $lesson_id, '_lesson_course', true ) );
518
			$url = add_query_arg( array( 'page' => $this->page_slug, 'course_id' => $course_id ), admin_url( 'admin.php' ) );
519
			$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 ) );
520
			$url = add_query_arg( array( 'page' => $this->page_slug, 'lesson_id' => $lesson_id ), admin_url( 'admin.php' ) );
521
			$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 ) );
522
		}
523
		?>
524
			<h1><?php echo apply_filters( 'sensei_analysis_nav_title', $title ); ?></h1>
525
		<?php
526
	} // End analysis_lesson_users_nav()
527
528
	/**
529
	 * Handles CSV export requests
530
	 * @since  1.2.0
531
	 * @return void
532
	 */
533
	public function report_download_page() {
534
		// Check if is a report
535
		if ( !empty( $_GET['sensei_report_download'] ) ) {
536
			$report = sanitize_text_field( $_GET['sensei_report_download'] );
537
538
			// Simple verification to ensure intent, Note that a Nonce is per user, so the URL can't be shared
539
			if ( !wp_verify_nonce( $_REQUEST['_sdl_nonce'], 'sensei_csv_download-' . $report ) ) {
540
				wp_die( __('Invalid request', 'woothemes-sensei') );
541
			}
542
543
			// Setup the variables we might need
544
			$filename = apply_filters( 'sensei_csv_export_filename', $report );
545
			$course_id = 0;
546
			$lesson_id = 0;
547
			$user_id = 0;
548
			if( isset( $_GET['course_id'] ) ) {
549
				$course_id = intval( $_GET['course_id'] );
550
			}
551
			if( isset( $_GET['lesson_id'] ) ) {
552
				$lesson_id = intval( $_GET['lesson_id'] );
553
			}
554
			if( isset( $_GET['user_id'] ) ) {
555
				$user_id = intval( $_GET['user_id'] );
556
			}
557
			$type = isset( $_GET['view'] ) ? esc_html( $_GET['view'] ) : false;
558
559
			if ( 0 < $lesson_id ) {
560
				// Viewing a specific Lesson and all its Learners
561
				$sensei_analysis_report_object = $this->load_report_object( 'Lesson', $lesson_id );
562
			}
563
			elseif ( 0 < $course_id && 0 < $user_id ) {
564
				// Viewing a specific User on a specific Course
565
				$sensei_analysis_report_object = $this->load_report_object( 'Course', $course_id, $user_id );
0 ignored issues
show
$user_id is of type integer, but the function expects a object<undefined>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
566
			}
567
			elseif( 0 < $course_id ) {
568
				// Viewing a specific Course and all it's Lessons, or it's Learners
569
				$sensei_analysis_report_object = $this->load_report_object( 'Course', $course_id );
570
			}
571
			elseif ( 0 < $user_id ) {
572
				// Viewing a specific Learner, and their Courses
573
				$sensei_analysis_report_object = $this->load_report_object( 'User_Profile', $user_id );
574
			}
575
			else {
576
				// Overview of all Learners, all Courses, or all Lessons
577
				$sensei_analysis_report_object = $this->load_report_object( 'Overview', $type );
578
			} // End If Statement
579
580
			// Handle the headers
581
			$this->report_set_headers( $filename );
582
583
			// Collate the data, there could be many different reports for a single object
584
			$report_data_array = $sensei_analysis_report_object->generate_report( $report );
585
586
			// Output the data
587
			$this->report_write_download( $report_data_array );
588
589
			// Cleanly exit
590
			exit;
591
		} // End wp_query check
592
	} // End report_download_page()
593
594
	/**
595
	 * Sets headers for CSV reporting export
596
	 * @since  1.2.0
597
	 * @param  string $filename name of report file
598
	 * @return void
599
	 */
600
	public function report_set_headers( $filename = '' ) {
601
		header( 'Content-Type: text/csv' );
602
		header( 'Content-Disposition: attachment;filename=' . $filename . '.csv');
603
	} // End report_set_headers()
604
605
	/**
606
	 * Loads the right object for CSV reporting
607
	 * @since  1.2.0
608
	 * @param  string  $name          Name of class
609
	 * @param  integer $data          constructor arguments
610
	 * @param  undefined  $optional_data optional constructor arguments
611
	 * @return object                 class instance object
612
	 */
613
	public function load_report_object( $name = '', $data = 0, $optional_data = null ) {
614
		$object_name = 'WooThemes_Sensei_Analysis_' . $name . '_List_Table';
615
		if ( is_null($optional_data) ) {
616
			$sensei_analysis_report_object = new $object_name( $data );
617
		} else {
618
			$sensei_analysis_report_object = new $object_name( $data, $optional_data );
619
		}
620
		return $sensei_analysis_report_object;
621
	} // End load_report_object()
622
623
	/**
624
	 * Write array data to CSV
625
	 * @since  1.2.0
626
	 * @param  array  $report_data data array
627
	 * @return void
628
	 */
629
	public function report_write_download( $report_data = array() ) {
630
		$fp = fopen('php://output', 'w');
631
		foreach ($report_data as $row) {
632
			fputcsv($fp, $row);
633
		} // End For Loop
634
		fclose($fp);
635
	} // End report_write_download()
636
637
	/**
638
	 * Adds display_name to the default list of search columns for the WP User Object
639
	 * @since  1.4.5
640
	 * @param  array $search_columns         array of default user columns to search
641
	 * @param  string $search                search string
642
	 * @param  object $user_query_object     WP_User_Query Object
643
	 * @return array $search_columns         array of user columns to search
644
	 */
645
	public function user_search_columns_filter( $search_columns, $search, $user_query_object ) {
0 ignored issues
show
The parameter $search 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...
The parameter $user_query_object 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...
646
		// Alter $search_columns to include the fields you want to search on
647
		array_push( $search_columns, 'display_name' );
648
		return $search_columns;
649
	}
650
651
} // End Class
652
653
/**
654
 * Class WooThemes_Sensei_Analysis
655
 * @ignore only for backward compatibility
656
 * @since 1.9.0
657
 */
658
class WooThemes_Sensei_Analysis extends Sensei_Analysis {}
659