Completed
Push — fix/site-plan-data-fetching ( 8cf155...9301ea )
by
unknown
17:09 queued 07:44
created

stats.php ➔ stats_reports_page()   F

Complexity

Conditions 30
Paths 1853

Size

Total Lines 119
Code Lines 94

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 30
eloc 94
nc 1853
nop 1
dl 0
loc 119
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Module Name: Site Stats
4
 * Module Description: Collect valuable traffic stats and insights.
5
 * Sort Order: 1
6
 * Recommendation Order: 2
7
 * First Introduced: 1.1
8
 * Requires Connection: Yes
9
 * Auto Activate: Yes
10
 * Module Tags: Site Stats, Recommended
11
 * Feature: Engagement
12
 * Additional Search Queries: statistics, tracking, analytics, views, traffic, stats
13
 */
14
15
if ( defined( 'STATS_VERSION' ) ) {
16
	return;
17
}
18
19
define( 'STATS_VERSION', '9' );
20
defined( 'STATS_DASHBOARD_SERVER' ) or define( 'STATS_DASHBOARD_SERVER', 'dashboard.wordpress.com' );
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
21
22
add_action( 'jetpack_modules_loaded', 'stats_load' );
23
24
function stats_load() {
25
	global $wp_roles;
26
27
	Jetpack::enable_module_configurable( __FILE__ );
28
	Jetpack::module_configuration_load( __FILE__, 'stats_configuration_load' );
29
	Jetpack::module_configuration_head( __FILE__, 'stats_configuration_head' );
30
	Jetpack::module_configuration_screen( __FILE__, 'stats_configuration_screen' );
31
32
	// Generate the tracking code after wp() has queried for posts.
33
	add_action( 'template_redirect', 'stats_template_redirect', 1 );
34
35
	add_action( 'wp_head', 'stats_admin_bar_head', 100 );
36
37
	add_action( 'wp_head', 'stats_hide_smile_css' );
38
39
	add_action( 'jetpack_admin_menu', 'stats_admin_menu' );
40
41
	// Map stats caps
42
	add_filter( 'map_meta_cap', 'stats_map_meta_caps', 10, 4 );
43
44
	if ( isset( $_GET['oldwidget'] ) ) {
45
		// Old one.
46
		add_action( 'wp_dashboard_setup', 'stats_register_dashboard_widget' );
47
	} else {
48
		add_action( 'admin_init', 'stats_merged_widget_admin_init' );
49
	}
50
51
	add_filter( 'jetpack_xmlrpc_methods', 'stats_xmlrpc_methods' );
52
53
54
	add_filter( 'pre_option_db_version', 'stats_ignore_db_version' );
55
}
56
57
/**
58
 * Delay conditional for current_user_can to after init.
59
 */
60
function stats_merged_widget_admin_init() {
61
	if ( current_user_can( 'view_stats' ) ) {
62
		add_action( 'load-index.php', 'stats_enqueue_dashboard_head' );
63
		add_action( 'wp_dashboard_setup', 'stats_register_widget_control_callback' ); // hacky but works
64
		add_action( 'jetpack_dashboard_widget', 'stats_jetpack_dashboard_widget' );
65
	}
66
}
67
68
function stats_enqueue_dashboard_head() {
69
	add_action( 'admin_head', 'stats_dashboard_head' );
70
}
71
72
/**
73
 * Prevent sparkline img requests being redirected to upgrade.php.
74
 * See wp-admin/admin.php where it checks $wp_db_version.
75
 */
76
function stats_ignore_db_version( $version ) {
77
	if (
78
		is_admin() &&
79
		isset( $_GET['page'] ) && $_GET['page'] == 'stats' &&
80
		isset( $_GET['chart'] ) && strpos($_GET['chart'], 'admin-bar-hours') === 0
81
	) {
82
		global $wp_db_version;
83
		return $wp_db_version;
84
	}
85
	return $version;
86
}
87
88
/**
89
 * Maps view_stats cap to read cap as needed
90
 *
91
 * @return array Possibly mapped capabilities for meta capability
92
 */
93
function stats_map_meta_caps( $caps, $cap, $user_id, $args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $args 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...
94
	// Map view_stats to exists
95
	if ( 'view_stats' == $cap ) {
96
		$user        = new WP_User( $user_id );
97
		$user_role   = array_shift( $user->roles );
98
		$stats_roles = stats_get_option( 'roles' );
99
100
		// Is the users role in the available stats roles?
101
		if ( is_array( $stats_roles ) && in_array( $user_role, $stats_roles ) ) {
102
			$caps = array( 'read' );
103
		}
104
	}
105
106
	return $caps;
107
}
108
109
function stats_template_redirect() {
110
	global $current_user, $stats_footer;
111
112
	if ( is_feed() || is_robots() || is_trackback() || is_preview() )
113
		return;
114
115
	// Should we be counting this user's views?
116
	if ( !empty( $current_user->ID ) ) {
117
		$count_roles = stats_get_option( 'count_roles' );
118
		if ( ! array_intersect( $current_user->roles, $count_roles ) )
119
			return;
120
	}
121
122
	add_action( 'wp_footer', 'stats_footer', 101 );
123
	add_action( 'wp_head', 'stats_add_shutdown_action' );
124
125
	$script = set_url_scheme( '//stats.wp.com/e-' . gmdate( 'YW' ) . '.js' );
126
	$data = stats_build_view_data();
127
	$data_stats_array = stats_array( $data );
128
129
	$stats_footer = <<<END
130
<script type='text/javascript' src='{$script}' async defer></script>
131
<script type='text/javascript'>
132
	_stq = window._stq || [];
133
	_stq.push([ 'view', {{$data_stats_array}} ]);
134
	_stq.push([ 'clickTrackerInit', '{$data['blog']}', '{$data['post']}' ]);
135
</script>
136
137
END;
138
}
139
140
function stats_build_view_data() {
141
	global $wp_the_query;
142
143
	$blog = Jetpack_Options::get_option( 'id' );
144
	$tz = get_option( 'gmt_offset' );
145
	$v = 'ext';
146
	$blog_url = parse_url( site_url() );
147
	$srv = $blog_url['host'];
148
	$j = sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION );
149
	if ( $wp_the_query->is_single || $wp_the_query->is_page || $wp_the_query->is_posts_page ) {
150
		// Store and reset the queried_object and queried_object_id
151
		// Otherwise, redirect_canonical() will redirect to home_url( '/' ) for show_on_front = page sites where home_url() is not all lowercase.
152
		// Repro:
153
		// 1. Set home_url = http://ExamPle.com/
154
		// 2. Set show_on_front = page
155
		// 3. Set page_on_front = something
156
		// 4. Visit http://example.com/
157
158
		$queried_object = ( isset( $wp_the_query->queried_object ) ) ? $wp_the_query->queried_object : null;
159
		$queried_object_id = ( isset( $wp_the_query->queried_object_id ) ) ? $wp_the_query->queried_object_id : null;
160
		$post = $wp_the_query->get_queried_object_id();
161
		$wp_the_query->queried_object = $queried_object;
162
		$wp_the_query->queried_object_id = $queried_object_id;
163
	} else {
164
		$post = '0';
165
	}
166
167
	return compact( 'v', 'j', 'blog', 'post', 'tz', 'srv' );
168
}
169
170
function stats_add_shutdown_action() {
171
	// just in case wp_footer isn't in your theme
172
	add_action( 'shutdown',  'stats_footer', 101 );
173
}
174
175
function stats_footer() {
176
	global $stats_footer;
177
	print $stats_footer;
178
	$stats_footer = '';
179
}
180
181
function stats_get_options() {
182
	$options = get_option( 'stats_options' );
183
184
	if ( !isset( $options['version'] ) || $options['version'] < STATS_VERSION )
185
		$options = stats_upgrade_options( $options );
186
187
	return $options;
188
}
189
190
function stats_get_option( $option ) {
191
	$options = stats_get_options();
192
193
	if ( $option == 'blog_id' )
194
		return Jetpack_Options::get_option( 'id' );
195
196
	if ( isset( $options[$option] ) )
197
		return $options[$option];
198
199
	return null;
200
}
201
202
function stats_set_option( $option, $value ) {
203
	$options = stats_get_options();
204
205
	$options[$option] = $value;
206
207
	stats_set_options($options);
208
}
209
210
function stats_set_options($options) {
211
	update_option( 'stats_options', $options );
212
}
213
214
function stats_upgrade_options( $options ) {
215
	$defaults = array(
216
		'admin_bar'    => true,
217
		'roles'        => array( 'administrator' ),
218
		'count_roles'  => array(),
219
		'blog_id'      => Jetpack_Options::get_option( 'id' ),
220
		'do_not_track' => true, // @todo
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
221
		'hide_smile'   => true,
222
	);
223
224
	if ( isset( $options['reg_users'] ) ) {
225
		if ( ! function_exists( 'get_editable_roles' ) )
226
			require_once( ABSPATH . 'wp-admin/includes/user.php' );
227
		if ( $options['reg_users'] )
228
			$options['count_roles'] = array_keys( get_editable_roles() );
229
		unset( $options['reg_users'] );
230
	}
231
232
	if ( is_array( $options ) && !empty( $options ) )
233
		$new_options = array_merge( $defaults, $options );
234
	else
235
		$new_options = $defaults;
236
237
	$new_options['version'] = STATS_VERSION;
238
239
	stats_set_options( $new_options );
240
241
	stats_update_blog();
242
243
	return $new_options;
244
}
245
246
function stats_array( $kvs ) {
247
	/**
248
	 * Filter the options added to the JavaScript Stats tracking code.
249
	 *
250
	 * @module stats
251
	 *
252
	 * @since 1.1.0
253
	 *
254
	 * @param array $kvs Array of options about the site and page you're on.
255
	 */
256
	$kvs = apply_filters( 'stats_array', $kvs );
257
	$kvs = array_map( 'addslashes', $kvs );
258
	foreach ( $kvs as $k => $v )
259
		$jskvs[] = "$k:'$v'";
0 ignored issues
show
Coding Style Comprehensibility introduced by
$jskvs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $jskvs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
260
	return join( ',', $jskvs );
0 ignored issues
show
Bug introduced by
The variable $jskvs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
261
}
262
263
/**
264
 * Admin Pages
265
 */
266
function stats_admin_menu() {
267
	global $pagenow;
268
269
	// If we're at an old Stats URL, redirect to the new one.
270
	// Don't even bother with caps, menu_page_url(), etc.  Just do it.
271
	if ( 'index.php' == $pagenow && isset( $_GET['page'] ) && 'stats' == $_GET['page'] ) {
272
		$redirect_url =	str_replace( array( '/wp-admin/index.php?', '/wp-admin/?' ), '/wp-admin/admin.php?', $_SERVER['REQUEST_URI'] );
273
		$relative_pos = strpos(	$redirect_url, '/wp-admin/' );
274
		if ( false !== $relative_pos ) {
275
			wp_safe_redirect( admin_url( substr( $redirect_url, $relative_pos + 10 ) ) );
276
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function stats_admin_menu() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
277
		}
278
	}
279
280
	$hook = add_submenu_page( 'jetpack', __( 'Site Stats', 'jetpack' ), __( 'Site Stats', 'jetpack' ), 'view_stats', 'stats', 'stats_reports_page' );
281
	add_action( "load-$hook", 'stats_reports_load' );
282
}
283
284
function stats_admin_path() {
285
	return Jetpack::module_configuration_url( __FILE__ );
286
}
287
288
function stats_reports_load() {
289
	wp_enqueue_script( 'jquery' );
290
	wp_enqueue_script( 'postbox' );
291
	wp_enqueue_script( 'underscore' );
292
293
	add_action( 'admin_print_styles', 'stats_reports_css' );
294
295
	if ( isset( $_GET['nojs'] ) && $_GET['nojs'] ) {
296
		$parsed = parse_url( admin_url() );
297
		// Remember user doesn't want JS
298
		setcookie( 'stnojs', '1', time() + 172800, $parsed['path'] ); // 2 days
299
	}
300
301
	if ( isset( $_COOKIE['stnojs'] ) && $_COOKIE['stnojs'] ) {
302
		// Detect if JS is on.  If so, remove cookie so next page load is via JS
303
		add_action( 'admin_print_footer_scripts', 'stats_js_remove_stnojs_cookie' );
304
	} else if ( !isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) ) {
305
		// Normal page load.  Load page content via JS.
306
		add_action( 'admin_print_footer_scripts', 'stats_js_load_page_via_ajax' );
307
	}
308
}
309
310
function stats_reports_css() {
311
?>
312
<style type="text/css">
313
#stats-loading-wrap p {
314
	text-align: center;
315
	font-size: 2em;
316
	margin: 7.5em 15px 0 0;
317
	height: 64px;
318
	line-height: 64px;
319
}
320
</style>
321
<?php
322
}
323
324
// Detect if JS is on.  If so, remove cookie so next page load is via JS.
325
function stats_js_remove_stnojs_cookie() {
326
	$parsed = parse_url( admin_url() );
327
?>
328
<script type="text/javascript">
329
/* <![CDATA[ */
330
document.cookie = 'stnojs=0; expires=Wed, 9 Mar 2011 16:55:50 UTC; path=<?php echo esc_js( $parsed['path'] ); ?>';
331
/* ]]> */
332
</script>
333
<?php
334
}
335
336
// Normal page load.  Load page content via JS.
337
function stats_js_load_page_via_ajax() {
338
?>
339
<script type="text/javascript">
340
/* <![CDATA[ */
341
if ( -1 == document.location.href.indexOf( 'noheader' ) ) {
342
	jQuery( function( $ ) {
343
		$.get( document.location.href + '&noheader', function( responseText ) {
344
			$( '#stats-loading-wrap' ).replaceWith( responseText );
345
		} );
346
	} );
347
}
348
/* ]]> */
349
</script>
350
<?php
351
}
352
353
function stats_reports_page( $main_chart_only = false ) {
354
	if ( isset( $_GET['dashboard'] ) )
355
		return stats_dashboard_widget_content();
356
357
	$blog_id = stats_get_option( 'blog_id' );
358
	$domain = Jetpack::build_raw_urls( get_home_url() );
359
360
	if ( ! $main_chart_only && !isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) && empty( $_COOKIE['stnojs'] ) ) {
361
		$nojs_url = add_query_arg( 'nojs', '1' );
362
		$http = is_ssl() ? 'https' : 'http';
363
		// Loading message
364
		// No JS fallback message
365
?>
366
<div class="wrap">
367
	<h2><?php esc_html_e( 'Site Stats', 'jetpack'); ?> <?php if ( current_user_can( 'jetpack_manage_modules' ) ) : ?><a style="font-size:13px;" href="<?php echo esc_url( admin_url('admin.php?page=jetpack&configure=stats') ); ?>"><?php esc_html_e( 'Configure', 'jetpack'); ?></a><?php endif; ?></h2>
368
</div>
369
<div id="stats-loading-wrap" class="wrap">
370
<p class="hide-if-no-js"><img width="32" height="32" alt="<?php esc_attr_e( 'Loading&hellip;', 'jetpack' ); ?>" src="<?php
371
/** This filter is documented in modules/shortcodes/audio.php */
372
echo esc_url( apply_filters( 'jetpack_static_url', "{$http}://en.wordpress.com/i/loading/loading-64.gif" ) ); ?>" /></p>
373
<p style="font-size: 11pt; margin: 0;"><a href="https://wordpress.com/stats/<?php echo $domain; ?>" target="_blank"><?php esc_html_e( 'View stats on WordPress.com right now', 'jetpack' ); ?></a></p>
374
<p class="hide-if-js"><?php esc_html_e( 'Your Site Stats work better with JavaScript enabled.', 'jetpack' ); ?><br />
375
<a href="<?php echo esc_url( $nojs_url ); ?>"><?php esc_html_e( 'View Site Stats without JavaScript', 'jetpack' ); ?></a>.</p>
376
</div>
377
<?php
378
		return;
379
	}
380
381
	$day = isset( $_GET['day'] ) && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $_GET['day'] ) ? $_GET['day'] : false;
382
	$q = array(
383
		'noheader' => 'true',
384
		'proxy' => '',
385
		'page' => 'stats',
386
		'day' => $day,
387
		'blog' => $blog_id,
388
		'charset' => get_option( 'blog_charset' ),
389
		'color' => get_user_option( 'admin_color' ),
390
		'ssl' => is_ssl(),
391
		'j' => sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ),
392
	);
393
	if ( get_locale() !== 'en_US' ) {
394
		$q['jp_lang'] = get_locale();
395
	}
396
	// Only show the main chart, without extra header data, or metaboxes.
397
	$q['main_chart_only'] = $main_chart_only;
398
	$args = array(
399
		'view' => array( 'referrers', 'postviews', 'searchterms', 'clicks', 'post', 'table' ),
400
		'numdays' => 'int',
401
		'day' => 'date',
402
		'unit' => array( 1, 7, 31, 'human' ),
403
		'humanize' => array( 'true' ),
404
		'num' => 'int',
405
		'summarize' => null,
406
		'post' => 'int',
407
		'width' => 'int',
408
		'height' => 'int',
409
		'data' => 'data',
410
		'blog_subscribers' => 'int',
411
		'comment_subscribers' => null,
412
		'type' => array( 'wpcom', 'email', 'pending' ),
413
		'pagenum' => 'int',
414
	);
415
	foreach ( $args as $var => $vals ) {
416
		if ( !isset( $_REQUEST[$var] ) )
417
			continue;
418
		if ( is_array( $vals ) ) {
419
			if ( in_array( $_REQUEST[$var], $vals ) )
420
				$q[$var] = $_REQUEST[$var];
421
		} elseif ( $vals == 'int' ) {
422
			$q[$var] = intval( $_REQUEST[$var] );
423
		} elseif ( $vals == 'date' ) {
424
			if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $_REQUEST[$var] ) )
425
				$q[$var] = $_REQUEST[$var];
426
		} elseif ( $vals == null ) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $vals of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
427
			$q[$var] = '';
428
		} elseif ( $vals == 'data' ) {
429
			if ( substr( $_REQUEST[$var], 0, 9 ) == 'index.php' )
430
				$q[$var] = $_REQUEST[$var];
431
		}
432
	}
433
434
	if ( isset( $_GET['chart'] ) ) {
435
		if ( preg_match( '/^[a-z0-9-]+$/', $_GET['chart'] ) ) {
436
			$chart = sanitize_title( $_GET['chart'] );
437
			$url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-includes/charts/{$chart}.php";
438
		}
439
	} else {
440
		$url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-admin/index.php";
441
	}
442
443
	$url = add_query_arg( $q, $url );
0 ignored issues
show
Bug introduced by
The variable $url does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
444
	$method = 'GET';
445
	$timeout = 90;
446
	$user_id = JETPACK_MASTER_USER; // means send the wp.com user_id
447
448
	$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
449
	$get_code = wp_remote_retrieve_response_code( $get );
450
	if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
451
		stats_print_wp_remote_error( $get, $url );
452
	} else {
453
		if ( !empty( $get['headers']['content-type'] ) ) {
454
			$type = $get['headers']['content-type'];
455
			if ( substr( $type, 0, 5 ) == 'image' ) {
456
				$img = $get['body'];
457
				header( 'Content-Type: ' . $type );
458
				header( 'Content-Length: ' . strlen( $img ) );
459
				echo $img;
460
				die();
0 ignored issues
show
Coding Style Compatibility introduced by
The function stats_reports_page() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
461
			}
462
		}
463
		$body = stats_convert_post_titles( $get['body'] );
464
		$body = stats_convert_chart_urls( $body );
465
		$body = stats_convert_image_urls( $body );
466
		$body = stats_convert_admin_urls( $body );
467
		echo $body;
468
	}
469
	if ( isset( $_GET['noheader'] ) )
470
		die;
0 ignored issues
show
Coding Style Compatibility introduced by
The function stats_reports_page() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
471
}
472
473
function stats_convert_admin_urls( $html ) {
474
	return str_replace( 'index.php?page=stats', 'admin.php?page=stats', $html );
475
}
476
477
function stats_convert_image_urls( $html ) {
478
	$url = set_url_scheme( 'https://' . STATS_DASHBOARD_SERVER );
479
	$html = preg_replace( '|(["\'])(/i/stats.+)\\1|', '$1' . $url . '$2$1', $html );
480
	return $html;
481
}
482
483
function stats_convert_chart_urls( $html ) {
484
	$html = preg_replace_callback( '|https?://[-.a-z0-9]+/wp-includes/charts/([-.a-z0-9]+).php(\??)|',
485
			create_function(
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
486
				'$matches',
487
				// If there is a query string, change the beginning '?' to a '&' so it fits into the middle of this query string
488
				'return "admin.php?page=stats&noheader&chart=" . $matches[1] . str_replace( "?", "&", $matches[2] );'
489
			),
490
			$html );
491
	return $html;
492
}
493
494
function stats_convert_post_titles( $html ) {
495
	global $wpdb, $stats_posts;
496
	$pattern = "<span class='post-(\d+)-link'>.*?</span>";
497
	if ( !preg_match_all( "!$pattern!", $html, $matches ) )
498
		return $html;
499
	$posts = get_posts( array(
500
		'include' => implode( ',', $matches[1] ),
501
		'post_type' => 'any',
502
		'post_status' => 'any',
503
		'numberposts' => -1,
504
	));
505
	foreach ( $posts as $post )
506
		$stats_posts[$post->ID] = $post;
507
	$html = preg_replace_callback( "!$pattern!", 'stats_convert_post_title', $html );
508
	return $html;
509
}
510
511
function stats_convert_post_title( $matches ) {
512
	global $stats_posts;
513
	$post_id = $matches[1];
514
	if ( isset( $stats_posts[$post_id] ) )
515
		return '<a href="' . get_permalink( $post_id ) . '" target="_blank">' . get_the_title( $post_id ) . '</a>';
516
	return $matches[0];
517
}
518
519
function stats_configuration_load() {
520
	if ( isset( $_POST['action'] ) && $_POST['action'] == 'save_options' && $_POST['_wpnonce'] == wp_create_nonce( 'stats' ) ) {
521
		$options = stats_get_options();
522
		$options['admin_bar']  = isset( $_POST['admin_bar']  ) && $_POST['admin_bar'];
523
		$options['hide_smile'] = isset( $_POST['hide_smile'] ) && $_POST['hide_smile'];
524
525
		$options['roles'] = array( 'administrator' );
526 View Code Duplication
		foreach ( get_editable_roles() as $role => $details )
527
			if ( isset( $_POST["role_$role"] ) && $_POST["role_$role"] )
528
				$options['roles'][] = $role;
529
530
		$options['count_roles'] = array();
531 View Code Duplication
		foreach ( get_editable_roles() as $role => $details )
532
			if ( isset( $_POST["count_role_$role"] ) && $_POST["count_role_$role"] )
533
				$options['count_roles'][] = $role;
534
535
		stats_set_options( $options );
536
		stats_update_blog();
537
		Jetpack::state( 'message', 'module_configured' );
538
		wp_safe_redirect( Jetpack::module_configuration_url( 'stats' ) );
539
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function stats_configuration_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
540
	}
541
}
542
543
function stats_configuration_head() {
544
	?>
545
	<style type="text/css">
546
		#statserror {
547
			border: 1px solid #766;
548
			background-color: #d22;
549
			padding: 1em 3em;
550
		}
551
		.stats-smiley {
552
			vertical-align: 1px;
553
		}
554
	</style>
555
	<?php
556
}
557
558
function stats_configuration_screen() {
559
	$options = stats_get_options();
560
	?>
561
	<div class="narrow">
562
		<p><?php printf( __( 'Visit <a href="%s">Site Stats</a> to see your stats.', 'jetpack' ), esc_url( menu_page_url( 'stats', false ) ) ); ?></p>
563
		<form method="post">
564
		<input type='hidden' name='action' value='save_options' />
565
		<?php wp_nonce_field( 'stats' ); ?>
566
		<table id="menu" class="form-table">
567
		<tr valign="top"><th scope="row"><label for="admin_bar"><?php _e( 'Admin bar' , 'jetpack' ); ?></label></th>
568
		<td><label><input type='checkbox'<?php checked( $options['admin_bar'] ); ?> name='admin_bar' id='admin_bar' /> <?php _e( "Put a chart showing 48 hours of views in the admin bar.", 'jetpack' ); ?></label></td></tr>
569
		<tr valign="top"><th scope="row"><?php _e( 'Registered users', 'jetpack' ); ?></th>
570
		<td>
571
			<?php _e( "Count the page views of registered users who are logged in.", 'jetpack' ); ?><br/>
572
			<?php
573
			$count_roles = stats_get_option( 'count_roles' );
574
			foreach ( get_editable_roles() as $role => $details ) {
575
				?>
576
				<label><input type='checkbox' name='count_role_<?php echo $role; ?>'<?php checked( in_array( $role, $count_roles ) ); ?> /> <?php echo translate_user_role( $details['name'] ); ?></label><br/>
577
				<?php
578
			}
579
			?>
580
		</td></tr>
581
		<tr valign="top"><th scope="row"><?php _e( 'Smiley' , 'jetpack' ); ?></th>
582
		<td><label><input type='checkbox'<?php checked( isset( $options['hide_smile'] ) && $options['hide_smile'] ); ?> name='hide_smile' id='hide_smile' /> <?php _e( 'Hide the stats smiley face image.', 'jetpack' ); ?></label><br /> <span class="description"><?php _e( 'The image helps collect stats and <strong>makes the world a better place</strong> but should still work when hidden', 'jetpack' ); ?> <img class="stats-smiley" alt="<?php esc_attr_e( 'Smiley face', 'jetpack' ); ?>" src="<?php echo esc_url( plugins_url( 'images/stats-smiley.gif', dirname( __FILE__ ) ) ); ?>" width="6" height="5" /></span></td></tr>
583
		<tr valign="top"><th scope="row"><?php _e( 'Report visibility' , 'jetpack' ); ?></th>
584
		<td>
585
			<?php _e( 'Select the roles that will be able to view stats reports.', 'jetpack' ); ?><br/>
586
			<?php
587
			$stats_roles = stats_get_option( 'roles' );
588
			foreach ( get_editable_roles() as $role => $details ) {
589
				?>
590
				<label><input type='checkbox' <?php if ( $role == 'administrator' ) echo "disabled='disabled' "; ?>name='role_<?php echo $role; ?>'<?php checked( $role == 'administrator' || in_array( $role, $stats_roles ) ); ?> /> <?php echo translate_user_role( $details['name'] ); ?></label><br/>
591
				<?php
592
			}
593
			?>
594
		</td></tr>
595
		</table>
596
		<p class="submit"><input type='submit' class='button-primary' value='<?php echo esc_attr( __( 'Save configuration', 'jetpack' ) ); ?>' /></p>
597
		</form>
598
	</div>
599
	<?php
600
}
601
602
function stats_hide_smile_css() {
603
	$options = stats_get_options();
604
	if ( isset( $options['hide_smile'] ) && $options['hide_smile'] ) {
605
	?>
606
<style type='text/css'>img#wpstats{display:none}</style><?php
607
	}
608
}
609
610
function stats_admin_bar_head() {
611
	if ( !stats_get_option( 'admin_bar' ) )
612
		return;
613
614
	if ( !current_user_can( 'view_stats' ) )
615
		return;
616
617
	if ( function_exists( 'is_admin_bar_showing' ) && !is_admin_bar_showing() ) {
618
		return;
619
	}
620
621
	add_action( 'admin_bar_menu', 'stats_admin_bar_menu', 100 );
622
	?>
623
624
<style type='text/css'>
625
#wpadminbar .quicklinks li#wp-admin-bar-stats {
626
	height: 28px;
627
}
628
#wpadminbar .quicklinks li#wp-admin-bar-stats a {
629
	height: 28px;
630
	padding: 0;
631
}
632
#wpadminbar .quicklinks li#wp-admin-bar-stats a div {
633
	height: 28px;
634
	width: 95px;
635
	overflow: hidden;
636
	margin: 0 10px;
637
}
638
#wpadminbar .quicklinks li#wp-admin-bar-stats a:hover div {
639
	width: auto;
640
	margin: 0 8px 0 10px;
641
}
642
#wpadminbar .quicklinks li#wp-admin-bar-stats a img {
643
	height: 24px;
644
	padding: 2px 0;
645
	max-width: none;
646
	border: none;
647
}
648
</style>
649
<?php
650
}
651
652
function stats_admin_bar_menu( &$wp_admin_bar ) {
653
	$url = add_query_arg( 'page', 'stats', admin_url( 'admin.php' ) ); // no menu_page_url() blog-side.
654
655
	$img_src = esc_attr( add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours-scale' ), $url ) );
656
	$img_src_2x = esc_attr( add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours-scale-2x' ), $url ) );
657
658
	$alt = esc_attr( __( 'Stats', 'jetpack' ) );
659
660
	$title = esc_attr( __( 'Views over 48 hours. Click for more Site Stats.', 'jetpack' ) );
661
662
	$menu = array( 'id' => 'stats', 'title' => "<div><script type='text/javascript'>var src;if(typeof(window.devicePixelRatio)=='undefined'||window.devicePixelRatio<2){src='$img_src';}else{src='$img_src_2x';}document.write('<img src=\''+src+'\' alt=\'$alt\' title=\'$title\' />');</script></div>", 'href' => $url );
663
664
	$wp_admin_bar->add_menu( $menu );
665
}
666
667
function stats_update_blog() {
668
	Jetpack::xmlrpc_async_call( 'jetpack.updateBlog', stats_get_blog() );
669
}
670
671
function stats_get_blog() {
672
	$home = parse_url( trailingslashit( get_option( 'home' ) ) );
673
	$blog = array(
674
		'host'                => $home['host'],
675
		'path'                => $home['path'],
676
		'blogname'            => get_option( 'blogname' ),
677
		'blogdescription'     => get_option( 'blogdescription' ),
678
		'siteurl'             => get_option( 'siteurl' ),
679
		'gmt_offset'          => get_option( 'gmt_offset' ),
680
		'timezone_string'     => get_option( 'timezone_string' ),
681
		'stats_version'       => STATS_VERSION,
682
		'stats_api'           => 'jetpack',
683
		'page_on_front'       => get_option( 'page_on_front' ),
684
		'permalink_structure' => get_option( 'permalink_structure' ),
685
		'category_base'       => get_option( 'category_base' ),
686
		'tag_base'            => get_option( 'tag_base' ),
687
	);
688
	$blog = array_merge( stats_get_options(), $blog );
689
	unset( $blog['roles'], $blog['blog_id'] );
690
	return stats_esc_html_deep( $blog );
691
}
692
693
/**
694
 * Modified from stripslashes_deep()
695
 */
696
function stats_esc_html_deep( $value ) {
697
	if ( is_array( $value ) ) {
698
		$value = array_map( 'stats_esc_html_deep', $value );
699
	} elseif ( is_object( $value ) ) {
700
		$vars = get_object_vars( $value );
701
		foreach ( $vars as $key => $data ) {
702
			$value->{$key} = stats_esc_html_deep( $data );
703
		}
704
	} elseif ( is_string( $value ) ) {
705
		$value = esc_html( $value );
706
	}
707
708
	return $value;
709
}
710
711
function stats_xmlrpc_methods( $methods ) {
712
	$my_methods = array(
713
		'jetpack.getBlog' => 'stats_get_blog',
714
	);
715
716
	return array_merge( $methods, $my_methods );
717
}
718
719
function stats_register_dashboard_widget() {
720
	if ( ! current_user_can( 'view_stats' ) )
721
		return;
722
723
	// wp_dashboard_empty: we load in the content after the page load via JS
724
	wp_add_dashboard_widget( 'dashboard_stats', __( 'Site Stats', 'jetpack' ), 'wp_dashboard_empty', 'stats_dashboard_widget_control' );
725
726
	add_action( 'admin_head', 'stats_dashboard_head' );
727
}
728
729
function stats_dashboard_widget_options() {
730
	$defaults = array( 'chart' => 1, 'top' => 1, 'search' => 7 );
731
	if ( ( !$options = get_option( 'stats_dashboard_widget' ) ) || !is_array( $options ) )
732
		$options = array();
733
734
	// Ignore obsolete option values
735
	$intervals = array( 1, 7, 31, 90, 365 );
736
	foreach ( array( 'top', 'search' ) as $key )
737
		if ( isset( $options[$key] ) && !in_array( $options[$key], $intervals ) )
738
			unset( $options[$key] );
739
740
	return array_merge( $defaults, $options );
741
}
742
743
function stats_dashboard_widget_control() {
744
	$periods   = array(
745
		'1' => __( 'day', 'jetpack' ),
746
		'7' => __( 'week', 'jetpack' ),
747
		'31' => __( 'month', 'jetpack' ),
748
	);
749
	$intervals = array(
750
		'1' => __( 'the past day', 'jetpack' ),
751
		'7' => __( 'the past week', 'jetpack' ),
752
		'31' => __( 'the past month', 'jetpack' ),
753
		'90' => __( 'the past quarter', 'jetpack' ),
754
		'365' => __( 'the past year', 'jetpack' ),
755
	);
756
	$defaults = array(
757
		'top' => 1,
758
		'search' => 7,
759
	);
760
761
	$options = stats_dashboard_widget_options();
762
763
	if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && isset( $_POST['widget_id'] ) && 'dashboard_stats' == $_POST['widget_id'] ) {
764
		if ( isset( $periods[ $_POST['chart'] ] ) )
765
			$options['chart'] = $_POST['chart'];
766
		foreach ( array( 'top', 'search' ) as $key ) {
767
			if ( isset( $intervals[ $_POST[$key] ] ) )
768
				$options[$key] = $_POST[$key];
769
			else
770
				$options[$key] = $defaults[$key];
771
		}
772
		update_option( 'stats_dashboard_widget', $options );
773
	}
774
	?>
775
	<p>
776
	<label for="chart"><?php _e( 'Chart stats by' , 'jetpack' ); ?></label>
777
	<select id="chart" name="chart">
778
	<?php
779
	foreach ( $periods as $val => $label ) {
780
		?>
781
		<option value="<?php echo $val; ?>"<?php selected( $val, $options['chart'] ); ?>><?php echo esc_html( $label ); ?></option>
782
		<?php
783
	}
784
	?>
785
	</select>.
786
	</p>
787
788
	<p>
789
	<label for="top"><?php _e( 'Show top posts over', 'jetpack' ); ?></label>
790
	<select id="top" name="top">
791
	<?php
792 View Code Duplication
	foreach ( $intervals as $val => $label ) {
793
		?>
794
		<option value="<?php echo $val; ?>"<?php selected( $val, $options['top'] ); ?>><?php echo esc_html( $label ); ?></option>
795
		<?php
796
	}
797
	?>
798
	</select>.
799
	</p>
800
801
	<p>
802
	<label for="search"><?php _e( 'Show top search terms over', 'jetpack' ); ?></label>
803
	<select id="search" name="search">
804
	<?php
805 View Code Duplication
	foreach ( $intervals as $val => $label ) {
806
		?>
807
		<option value="<?php echo $val; ?>"<?php selected( $val, $options['search'] ); ?>><?php echo esc_html( $label ); ?></option>
808
		<?php
809
	}
810
	?>
811
	</select>.
812
	</p>
813
	<?php
814
}
815
816
function stats_jetpack_dashboard_widget() {
817
	?>
818
	<form id="stats_dashboard_widget_control" action="<?php echo esc_url( admin_url() ); ?>" method="post">
819
		<?php stats_dashboard_widget_control(); ?>
820
		<?php wp_nonce_field( 'edit-dashboard-widget_dashboard_stats', 'dashboard-widget-nonce' ); ?>
821
		<input type="hidden" name="widget_id" value="dashboard_stats" />
822
		<?php submit_button( __( 'Submit', 'jetpack' ) ); ?>
823
	</form>
824
	<span id="js-toggle-stats_dashboard_widget_control">
825
		<?php esc_html_e( 'Configure', 'jetpack' ); ?>
826
	</span>
827
	<div id="dashboard_stats">
828
		<div class="inside">
829
			<div style="height: 250px;"></div>
830
		</div>
831
	</div>
832
	<script>
833
		jQuery(document).ready(function($){
834
			var $toggle = $('#js-toggle-stats_dashboard_widget_control');
835
836
			$toggle.parent().prev().append( $toggle );
837
			$toggle.show().click(function(e){
838
				e.preventDefault();
839
				e.stopImmediatePropagation();
840
				$(this).parent().toggleClass('controlVisible');
841
				$('#stats_dashboard_widget_control').slideToggle();
842
			});
843
		});
844
	</script>
845
	<style>
846
		#js-toggle-stats_dashboard_widget_control {
847
			display: none;
848
			float: right;
849
			margin-top: 0.2em;
850
			font-weight: 400;
851
			color: #444;
852
			font-size: .8em;
853
			text-decoration: underline;
854
			cursor: pointer;
855
		}
856
		#stats_dashboard_widget_control {
857
			display: none;
858
			padding: 0 10px;
859
			overflow: hidden;
860
		}
861
		#stats_dashboard_widget_control .button-primary {
862
			float: right;
863
		}
864
		#dashboard_stats {
865
			box-sizing: border-box;
866
			width: 100%;
867
			padding: 0 10px;
868
		}
869
	</style>
870
	<?php
871
}
872
873
function stats_register_widget_control_callback() {
874
	$GLOBALS['wp_dashboard_control_callbacks']['dashboard_stats'] = 'stats_dashboard_widget_control';
875
}
876
// JavaScript and CSS for dashboard widget
877
function stats_dashboard_head() { ?>
878
<script type="text/javascript">
879
/* <![CDATA[ */
880
jQuery( function($) {
881
	var dashStats = jQuery( '#dashboard_stats div.inside' );
882
883
	if ( dashStats.find( '.dashboard-widget-control-form' ).length ) {
884
		return;
885
	}
886
887
	if ( ! dashStats.length ) {
888
		dashStats = jQuery( '#dashboard_stats div.dashboard-widget-content' );
889
		var h = parseInt( dashStats.parent().height() ) - parseInt( dashStats.prev().height() );
890
		var args = 'width=' + dashStats.width() + '&height=' + h.toString();
891
	} else {
892
		if ( jQuery('#dashboard_stats' ).hasClass('postbox') ) {
893
			var args = 'width=' + ( dashStats.prev().width() * 2 ).toString();
894
		} else {
895
			var args = 'width=' + ( dashStats.width() * 2 ).toString();
896
		}
897
	}
898
899
	dashStats
900
		.not( '.dashboard-widget-control' )
901
		.load( 'admin.php?page=stats&noheader&dashboard&' + args );
902
903
	jQuery( window ).one( 'resize', function() {
904
		jQuery( '#stat-chart' ).css( 'width', 'auto' );
905
	} );
906
} );
907
/* ]]> */
908
</script>
909
<style type="text/css">
910
/* <![CDATA[ */
911
#stat-chart {
912
	background: none !important;
913
}
914
#dashboard_stats .inside {
915
	margin: 10px 0 0 0 !important;
916
}
917
#dashboard_stats #stats-graph {
918
	margin: 0;
919
}
920
#stats-info {
921
	border-top: 1px solid #dfdfdf;
922
	margin: 7px -10px 0 -10px;
923
	padding: 10px;
924
	background: #fcfcfc;
925
	-moz-box-shadow:inset 0 1px 0 #fff;
926
	-webkit-box-shadow:inset 0 1px 0 #fff;
927
	box-shadow:inset 0 1px 0 #fff;
928
	overflow: hidden;
929
	border-radius: 0 0 2px 2px;
930
	-webkit-border-radius: 0 0 2px 2px;
931
	-moz-border-radius: 0 0 2px 2px;
932
	-khtml-border-radius: 0 0 2px 2px;
933
}
934
#stats-info #top-posts, #stats-info #top-search {
935
	float: left;
936
	width: 50%;
937
}
938
#top-posts .stats-section-inner p {
939
	white-space: nowrap;
940
	overflow: hidden;
941
}
942
#top-posts .stats-section-inner p a {
943
	overflow: hidden;
944
	text-overflow: ellipsis;
945
}
946
#stats-info div#active {
947
	border-top: 1px solid #dfdfdf;
948
	margin: 0 -10px;
949
	padding: 10px 10px 0 10px;
950
	-moz-box-shadow:inset 0 1px 0 #fff;
951
	-webkit-box-shadow:inset 0 1px 0 #fff;
952
	box-shadow:inset 0 1px 0 #fff;
953
	overflow: hidden;
954
}
955
#top-search p {
956
	color: #999;
957
}
958
#stats-info h3 {
959
	font-size: 1em;
960
	margin: 0 0 .5em 0 !important;
961
}
962
#stats-info p {
963
	margin: 0 0 .25em;
964
	color: #999;
965
}
966
#stats-info p.widget-loading {
967
	margin: 1em 0 0;
968
	color: #333;
969
}
970
#stats-info p a {
971
	display: block;
972
}
973
#stats-info p a.button {
974
	display: inline;
975
}
976
/* ]]> */
977
</style>
978
<?php
979
}
980
981
function stats_dashboard_widget_content() {
982
	if ( !isset( $_GET['width'] ) || ( !$width  = (int) ( $_GET['width'] / 2 ) ) || $width  < 250 )
983
		$width  = 370;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
984
	if ( !isset( $_GET['height'] ) || ( !$height = (int) $_GET['height'] - 36 )   || $height < 230 )
985
		$height = 180;
986
987
	$_width  = $width  - 5;
988
	$_height = $height - ( $GLOBALS['is_winIE'] ? 16 : 5 ); // hack!
989
990
	$options = stats_dashboard_widget_options();
991
	$blog_id = Jetpack_Options::get_option( 'id' );
992
993
	$q = array(
994
		'noheader' => 'true',
995
		'proxy' => '',
996
		'blog' => $blog_id,
997
		'page' => 'stats',
998
		'chart' => '',
999
		'unit' => $options['chart'],
1000
		'color' => get_user_option( 'admin_color' ),
1001
		'width' => $_width,
1002
		'height' => $_height,
1003
		'ssl' => is_ssl(),
1004
		'j' => sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ),
1005
	);
1006
1007
	$url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-admin/index.php";
1008
1009
	$url = add_query_arg( $q, $url );
1010
	$method = 'GET';
1011
	$timeout = 90;
1012
	$user_id = JETPACK_MASTER_USER;
1013
1014
	$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1015
	$get_code = wp_remote_retrieve_response_code( $get );
1016
	if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
1017
		stats_print_wp_remote_error( $get, $url );
1018
	} else {
1019
		$body = stats_convert_post_titles($get['body']);
1020
		$body = stats_convert_chart_urls($body);
1021
		$body = stats_convert_image_urls($body);
1022
		echo $body;
1023
	}
1024
1025
	$post_ids = array();
1026
1027
	$csv_end_date = date( 'Y-m-d', current_time( 'timestamp' ) );
1028
	$csv_args = array( 'top' => "&limit=8&end=$csv_end_date", 'search' => "&limit=5&end=$csv_end_date" );
1029
	/* translators: Stats dashboard widget postviews list: "$post_title $views Views" */
1030
	$printf = __( '%1$s %2$s Views' , 'jetpack' );
1031
1032
	foreach ( $top_posts = stats_get_csv( 'postviews', "days=$options[top]$csv_args[top]" ) as $i => $post ) {
1033
		if ( $post['post_id'] == 0 ) {
1034
			unset( $top_posts[$i] );
1035
			continue;
1036
		}
1037
		$post_ids[] = $post['post_id'];
1038
	}
1039
1040
	// cache
1041
	get_posts( array( 'include' => join( ',', array_unique( $post_ids ) ) ) );
1042
1043
	$searches = array();
1044
	foreach ( $search_terms = stats_get_csv( 'searchterms', "days=$options[search]$csv_args[search]" ) as $search_term ) {
1045
		if ( $search_term['searchterm'] == 'encrypted_search_terms' )
1046
			continue;
1047
		$searches[] = esc_html( $search_term['searchterm'] );
1048
	}
1049
1050
?>
1051
<a class="button" href="admin.php?page=stats"><?php _e( 'View All', 'jetpack' ); ?></a>
1052
<div id="stats-info">
1053
	<div id="top-posts" class='stats-section'>
1054
		<div class="stats-section-inner">
1055
		<h3 class="heading"><?php _e( 'Top Posts' , 'jetpack' ); ?></h3>
1056
		<?php
1057
		if ( empty( $top_posts ) ) {
1058
			?>
1059
			<p class="nothing"><?php _e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1060
			<?php
1061
		} else {
1062
			foreach ( $top_posts as $post ) {
1063
				if ( !get_post( $post['post_id'] ) )
1064
					continue;
1065
				?>
1066
				<p><?php printf(
1067
					$printf,
1068
					'<a href="' . get_permalink( $post['post_id'] ) . '">' . get_the_title( $post['post_id'] ) . '</a>',
1069
					number_format_i18n( $post['views'] )
1070
				); ?></p>
1071
				<?php
1072
			}
1073
		}
1074
		?>
1075
		</div>
1076
	</div>
1077
	<div id="top-search" class='stats-section'>
1078
		<div class="stats-section-inner">
1079
		<h3 class="heading"><?php _e( 'Top Searches' , 'jetpack' ); ?></h3>
1080
		<?php
1081
		if ( empty( $searches ) ) {
1082
			?>
1083
			<p class="nothing"><?php _e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1084
			<?php
1085
		} else {
1086
			?>
1087
			<p><?php echo join( ',&nbsp; ', $searches );?></p>
1088
			<?php
1089
		}
1090
		?>
1091
		</div>
1092
	</div>
1093
</div>
1094
<div class="clear"></div>
1095
<?php
1096
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function stats_dashboard_widget_content() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1097
}
1098
1099
function stats_print_wp_remote_error( $get, $url ) {
1100
	$state_name = 'stats_remote_error_' . substr( md5( $url ), 0, 8 );
1101
	$previous_error = Jetpack::state( $state_name );
1102
	$error = md5( serialize( compact( 'get', 'url' ) ) );
1103
	Jetpack::state( $state_name, $error );
1104
	if ( $error !== $previous_error ) {
1105
?>
1106
	<div class="wrap">
1107
	<p><?php _e( 'We were unable to get your stats just now. Please reload this page to try again.', 'jetpack' ); ?></p>
1108
	</div>
1109
<?php
1110
		return;
1111
	}
1112
?>
1113
	<div class="wrap">
1114
	<p><?php printf( __( 'We were unable to get your stats just now. Please reload this page to try again. If this error persists, please <a href="%1$s" target="_blank">contact support</a>. In your report please include the information below.', 'jetpack' ), 'http://support.wordpress.com/contact/?jetpack=needs-service' ); ?></p>
1115
	<pre>
1116
	User Agent: "<?php echo esc_html( $_SERVER['HTTP_USER_AGENT'] ); ?>"
1117
	Page URL: "http<?php echo (is_ssl()?'s':'') . '://' . esc_html( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); ?>"
1118
	API URL: "<?php echo esc_url( $url ); ?>"
1119
<?php
1120
	if ( is_wp_error( $get ) ) {
1121
		foreach ( $get->get_error_codes() as $code ) {
1122
			foreach ( $get->get_error_messages($code) as $message ) {
1123
				?>
1124
	<?php print $code . ': "' . $message . '"' ?>
1125
1126
<?php
1127
			}
1128
		}
1129
	} else {
1130
		$get_code = wp_remote_retrieve_response_code( $get );
1131
		$content_length = strlen( wp_remote_retrieve_body( $get ) );
1132
		?>
1133
	Response code: "<?php print $get_code ?>"
1134
	Content length: "<?php print $content_length ?>"
1135
1136
<?php
1137
	}
1138
	?></pre>
1139
	</div>
1140
	<?php
1141
}
1142
1143
/**
1144
 * Get stats from WordPress.com
1145
 *
1146
 * @param string $table The stats which you want to retrieve: postviews, or searchterms
1147
 * @param array $args {
0 ignored issues
show
Documentation introduced by
Should the type for parameter $args not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1148
 *     An associative array of arguments.
1149
 *
1150
 *      @type bool    $end        The last day of the desired time frame. Format is 'Y-m-d' (e.g. 2007-05-01)
1151
 *                                and default timezone is UTC date. Default value is Now.
1152
 *      @type string  $days       The length of the desired time frame. Default is 30. Maximum 90 days.
1153
 *      @type int     $limit      The maximum number of records to return. Default is 10. Maximum 100.
1154
 *      @type int     $post_id    The ID of the post to retrieve stats data for
1155
 *      @type string  $summarize  If present, summarizes all matching records. Default Null.
1156
 *
1157
 * }
1158
 *
1159
 * @return array {
1160
 *      An array of post view data, each post as an array
1161
 *
1162
 *      array {
1163
 *          The post view data for a single post
1164
 *
1165
 *          @type string  $post_id         The ID of the post
1166
 *          @type string  $post_title      The title of the post
1167
 *          @type string  $post_permalink  The permalink for the post
1168
 *          @type string  $views           The number of views for the post within the $num_days specified
1169
 *      }
1170
 * }
1171
 */
1172
function stats_get_csv( $table, $args = null ) {
1173
	$defaults = array( 'end' => false, 'days' => false, 'limit' => 3, 'post_id' => false, 'summarize' => '' );
1174
1175
	$args = wp_parse_args( $args, $defaults );
1176
	$args['table'] = $table;
1177
	$args['blog_id'] = Jetpack_Options::get_option( 'id' );
1178
1179
	$stats_csv_url = add_query_arg( $args, 'http://stats.wordpress.com/csv.php' );
1180
1181
	$key = md5( $stats_csv_url );
1182
1183
	// Get cache
1184
	$stats_cache = get_option( 'stats_cache' );
1185
	if ( !$stats_cache || !is_array( $stats_cache ) )
1186
		$stats_cache = array();
1187
1188
	// Return or expire this key
1189
	if ( isset( $stats_cache[$key] ) ) {
1190
		$time = key( $stats_cache[$key] );
1191
		if ( time() - $time < 300 )
1192
			return $stats_cache[$key][$time];
1193
		unset( $stats_cache[$key] );
1194
	}
1195
1196
	$stats_rows = array();
1197
	do {
1198
		if ( !$stats = stats_get_remote_csv( $stats_csv_url ) )
1199
			break;
1200
1201
		$labels = array_shift( $stats );
1202
1203
		if ( 0 === stripos( $labels[0], 'error' ) )
1204
			break;
1205
1206
		$stats_rows = array();
1207
		for ( $s = 0; isset( $stats[$s] ); $s++ ) {
1208
			$row = array();
1209
			foreach ( $labels as $col => $label )
1210
				$row[$label] = $stats[$s][$col];
1211
			$stats_rows[] = $row;
1212
		}
1213
	} while( 0 );
1214
1215
	// Expire old keys
1216 View Code Duplication
	foreach ( $stats_cache as $k => $cache )
1217
		if ( !is_array( $cache ) || 300 < time() - key($cache) )
1218
			unset( $stats_cache[$k] );
1219
1220
	// Set cache
1221
	$stats_cache[$key] = array( time() => $stats_rows );
1222
	update_option( 'stats_cache', $stats_cache );
1223
1224
	return $stats_rows;
1225
}
1226
1227
function stats_get_remote_csv( $url ) {
1228
	$method = 'GET';
1229
	$timeout = 90;
1230
	$user_id = JETPACK_MASTER_USER;
1231
1232
	$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1233
	$get_code = wp_remote_retrieve_response_code( $get );
1234
	if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
1235
		return array(); // @todo: return an error?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1236
	} else {
1237
		return stats_str_getcsv( $get['body'] );
1238
	}
1239
}
1240
1241
// rather than parsing the csv and its special cases, we create a new file and do fgetcsv on it.
1242
function stats_str_getcsv( $csv ) {
1243
	if ( function_exists( 'str_getcsv' ) ) {
1244
		$lines = str_getcsv( $csv, "\n" );
1245
		return array_map( 'str_getcsv', $lines );
1246
	}
1247
	if ( !$temp = tmpfile() ) // tmpfile() automatically unlinks
1248
		return false;
1249
1250
	$data = array();
1251
1252
	fwrite( $temp, $csv, strlen( $csv ) );
1253
	fseek( $temp, 0 );
1254
	while ( false !== $row = fgetcsv( $temp, 2000 ) )
1255
		$data[] = $row;
1256
	fclose( $temp );
1257
1258
	return $data;
1259
}
1260
1261
/**
1262
 * Abstract out building the rest api stats path.
1263
 *
1264
 * @param  string $resource
1265
 * @return string
1266
 */
1267
function jetpack_stats_api_path( $resource = '' ) {
1268
	$resource = ltrim( $resource, '/' );
1269
	return sprintf( '/sites/%d/stats/%s', stats_get_option( 'blog_id' ), $resource );
1270
}
1271
1272
/**
1273
 * Fetches stats data from the REST API.  Caches locally for 5 minutes.
1274
 *
1275
 * @link: https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/stats/
1276
 *
1277
 * @param  array|string   $args     The args that are passed to the endpoint
1278
 * @param  string         $resource Optional sub-endpoint following /stats/
1279
 * @return array|WP_Error
1280
 */
1281
function stats_get_from_restapi( $args = array(), $resource = '' ) {
1282
	$endpoint    = jetpack_stats_api_path( $resource );
1283
	$api_version = '1.1';
1284
	$args        = wp_parse_args( $args, array() );
1285
	$cache_key   = md5( implode( '|', array( $endpoint, $api_version, serialize( $args ) ) ) );
1286
1287
	// Get cache
1288
	$stats_cache = Jetpack_Options::get_option( 'restapi_stats_cache', array() );
1289
	if ( ! is_array( $stats_cache ) ) {
1290
		$stats_cache = array();
1291
	}
1292
1293
	// Return or expire this key
1294
	if ( isset( $stats_cache[ $cache_key ] ) ) {
1295
		$time = key( $stats_cache[ $cache_key ] );
1296
		if ( time() - $time < ( 5 * MINUTE_IN_SECONDS ) ) {
1297
			$cached_stats = $stats_cache[ $cache_key ][ $time ];
1298
			$cached_stats = (object) array_merge( array( 'cached_at' => $time ), (array) $cached_stats );
1299
			return $cached_stats;
1300
		}
1301
		unset( $stats_cache[ $cache_key ] );
1302
	}
1303
1304
	// Do the dirty work.
1305
	$response = Jetpack_Client::wpcom_json_api_request_as_blog( $endpoint, $api_version, $args );
1306
	if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
1307
		// If bad, just return it, don't cache.
1308
		return $response;
1309
	}
1310
1311
	$data = json_decode( wp_remote_retrieve_body( $response ) );
1312
1313
	// Expire old keys
1314 View Code Duplication
	foreach ( $stats_cache as $k => $cache ) {
1315
		if ( ! is_array( $cache ) || ( 5 * MINUTE_IN_SECONDS ) < time() - key( $cache ) ) {
1316
			unset( $stats_cache[ $k ] );
1317
		}
1318
	}
1319
1320
	// Set cache
1321
	$stats_cache[ $cache_key ] = array(
1322
		time() => $data,
1323
	);
1324
	Jetpack_Options::update_option( 'restapi_stats_cache', $stats_cache, false );
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|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...
1325
1326
	return $data;
1327
}
1328