Completed
Push — update/admin-page-readme-more-... ( 8f2301...4565b6 )
by
unknown
11:51
created

stats.php ➔ stats_upgrade_options()   C

Complexity

Conditions 7
Paths 20

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 23
nc 20
nop 1
dl 0
loc 33
rs 6.7272
c 0
b 0
f 0
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
188
	return $options;
189
}
190
191
function stats_get_option( $option ) {
192
	$options = stats_get_options();
193
194
	if ( $option == 'blog_id' )
195
		return Jetpack_Options::get_option( 'id' );
196
197
	if ( isset( $options[$option] ) )
198
		return $options[$option];
199
200
	return null;
201
}
202
203
function stats_set_option( $option, $value ) {
204
	$options = stats_get_options();
205
206
	$options[$option] = $value;
207
208
	return stats_set_options($options);
209
}
210
211
function stats_set_options($options) {
212
	return update_option( 'stats_options', $options );
213
}
214
215
function stats_upgrade_options( $options ) {
216
	$defaults = array(
217
		'admin_bar'    => true,
218
		'roles'        => array( 'administrator' ),
219
		'count_roles'  => array(),
220
		'blog_id'      => Jetpack_Options::get_option( 'id' ),
221
		'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...
222
		'hide_smile'   => true,
223
	);
224
225
	if ( isset( $options['reg_users'] ) ) {
226
		if ( ! function_exists( 'get_editable_roles' ) )
227
			require_once( ABSPATH . 'wp-admin/includes/user.php' );
228
		if ( $options['reg_users'] )
229
			$options['count_roles'] = array_keys( get_editable_roles() );
230
		unset( $options['reg_users'] );
231
	}
232
233
	if ( is_array( $options ) && !empty( $options ) )
234
		$new_options = array_merge( $defaults, $options );
235
	else
236
		$new_options = $defaults;
237
238
	$new_options['version'] = STATS_VERSION;
239
240
	if ( !  stats_set_options( $new_options ) ) {
241
		return false;
242
	}
243
244
	stats_update_blog();
245
246
	return $new_options;
247
}
248
249
function stats_array( $kvs ) {
250
	/**
251
	 * Filter the options added to the JavaScript Stats tracking code.
252
	 *
253
	 * @module stats
254
	 *
255
	 * @since 1.1.0
256
	 *
257
	 * @param array $kvs Array of options about the site and page you're on.
258
	 */
259
	$kvs = apply_filters( 'stats_array', $kvs );
260
	$kvs = array_map( 'addslashes', $kvs );
261
	foreach ( $kvs as $k => $v )
262
		$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...
263
	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...
264
}
265
266
/**
267
 * Admin Pages
268
 */
269
function stats_admin_menu() {
270
	global $pagenow;
271
272
	// If we're at an old Stats URL, redirect to the new one.
273
	// Don't even bother with caps, menu_page_url(), etc.  Just do it.
274
	if ( 'index.php' == $pagenow && isset( $_GET['page'] ) && 'stats' == $_GET['page'] ) {
275
		$redirect_url =	str_replace( array( '/wp-admin/index.php?', '/wp-admin/?' ), '/wp-admin/admin.php?', $_SERVER['REQUEST_URI'] );
276
		$relative_pos = strpos(	$redirect_url, '/wp-admin/' );
277
		if ( false !== $relative_pos ) {
278
			wp_safe_redirect( admin_url( substr( $redirect_url, $relative_pos + 10 ) ) );
279
			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...
280
		}
281
	}
282
283
	$hook = add_submenu_page( null, __( 'Site Stats', 'jetpack' ), __( 'Site Stats', 'jetpack' ), 'view_stats', 'stats', 'stats_reports_page' );
284
	add_action( "load-$hook", 'stats_reports_load' );
285
}
286
287
function stats_admin_path() {
288
	return Jetpack::module_configuration_url( __FILE__ );
289
}
290
291
function stats_reports_load() {
292
	wp_enqueue_script( 'jquery' );
293
	wp_enqueue_script( 'postbox' );
294
	wp_enqueue_script( 'underscore' );
295
296
	add_action( 'admin_print_styles', 'stats_reports_css' );
297
298
	if ( isset( $_GET['nojs'] ) && $_GET['nojs'] ) {
299
		$parsed = parse_url( admin_url() );
300
		// Remember user doesn't want JS
301
		setcookie( 'stnojs', '1', time() + 172800, $parsed['path'] ); // 2 days
302
	}
303
304
	if ( isset( $_COOKIE['stnojs'] ) && $_COOKIE['stnojs'] ) {
305
		// Detect if JS is on.  If so, remove cookie so next page load is via JS
306
		add_action( 'admin_print_footer_scripts', 'stats_js_remove_stnojs_cookie' );
307
	} else if ( !isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) ) {
308
		// Normal page load.  Load page content via JS.
309
		add_action( 'admin_print_footer_scripts', 'stats_js_load_page_via_ajax' );
310
	}
311
}
312
313
function stats_reports_css() {
314
?>
315
<style type="text/css">
316
#stats-loading-wrap p {
317
	text-align: center;
318
	font-size: 2em;
319
	margin: 7.5em 15px 0 0;
320
	height: 64px;
321
	line-height: 64px;
322
}
323
</style>
324
<?php
325
}
326
327
// Detect if JS is on.  If so, remove cookie so next page load is via JS.
328
function stats_js_remove_stnojs_cookie() {
329
	$parsed = parse_url( admin_url() );
330
?>
331
<script type="text/javascript">
332
/* <![CDATA[ */
333
document.cookie = 'stnojs=0; expires=Wed, 9 Mar 2011 16:55:50 UTC; path=<?php echo esc_js( $parsed['path'] ); ?>';
334
/* ]]> */
335
</script>
336
<?php
337
}
338
339
// Normal page load.  Load page content via JS.
340
function stats_js_load_page_via_ajax() {
341
?>
342
<script type="text/javascript">
343
/* <![CDATA[ */
344
if ( -1 == document.location.href.indexOf( 'noheader' ) ) {
345
	jQuery( function( $ ) {
346
		$.get( document.location.href + '&noheader', function( responseText ) {
347
			$( '#stats-loading-wrap' ).replaceWith( responseText );
348
		} );
349
	} );
350
}
351
/* ]]> */
352
</script>
353
<?php
354
}
355
356
function stats_reports_page( $main_chart_only = false ) {
357
	if ( isset( $_GET['dashboard'] ) )
358
		return stats_dashboard_widget_content();
359
360
	$blog_id = stats_get_option( 'blog_id' );
361
	$domain = Jetpack::build_raw_urls( get_home_url() );
362
363
	JetpackTracking::record_user_event( 'wpa_page_view', array( 'path' => 'old_stats' ) );
364
365
	if ( ! $main_chart_only && !isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) && empty( $_COOKIE['stnojs'] ) ) {
366
		$nojs_url = add_query_arg( 'nojs', '1' );
367
		$http = is_ssl() ? 'https' : 'http';
368
		// Loading message
369
		// No JS fallback message
370
?>
371
<div class="wrap">
372
	<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>
373
</div>
374
<div id="stats-loading-wrap" class="wrap">
375
<p class="hide-if-no-js"><img width="32" height="32" alt="<?php esc_attr_e( 'Loading&hellip;', 'jetpack' ); ?>" src="<?php
376
echo esc_url(
377
	/**
378
	 * Sets external resource URL.
379
	 *
380
	 * @module stats
381
	 *
382
	 * @since 1.4.0
383
	 *
384
	 * @param string $args URL of external resource.
385
	 */
386
	apply_filters( 'jetpack_static_url', "{$http}://en.wordpress.com/i/loading/loading-64.gif" )
387
); ?>" /></p>
388
<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>
389
<p class="hide-if-js"><?php esc_html_e( 'Your Site Stats work better with JavaScript enabled.', 'jetpack' ); ?><br />
390
<a href="<?php echo esc_url( $nojs_url ); ?>"><?php esc_html_e( 'View Site Stats without JavaScript', 'jetpack' ); ?></a>.</p>
391
</div>
392
<?php
393
		return;
394
	}
395
396
	$day = isset( $_GET['day'] ) && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $_GET['day'] ) ? $_GET['day'] : false;
397
	$q = array(
398
		'noheader' => 'true',
399
		'proxy' => '',
400
		'page' => 'stats',
401
		'day' => $day,
402
		'blog' => $blog_id,
403
		'charset' => get_option( 'blog_charset' ),
404
		'color' => get_user_option( 'admin_color' ),
405
		'ssl' => is_ssl(),
406
		'j' => sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ),
407
	);
408
	if ( get_locale() !== 'en_US' ) {
409
		$q['jp_lang'] = get_locale();
410
	}
411
	// Only show the main chart, without extra header data, or metaboxes.
412
	$q['main_chart_only'] = $main_chart_only;
413
	$args = array(
414
		'view' => array( 'referrers', 'postviews', 'searchterms', 'clicks', 'post', 'table' ),
415
		'numdays' => 'int',
416
		'day' => 'date',
417
		'unit' => array( 1, 7, 31, 'human' ),
418
		'humanize' => array( 'true' ),
419
		'num' => 'int',
420
		'summarize' => null,
421
		'post' => 'int',
422
		'width' => 'int',
423
		'height' => 'int',
424
		'data' => 'data',
425
		'blog_subscribers' => 'int',
426
		'comment_subscribers' => null,
427
		'type' => array( 'wpcom', 'email', 'pending' ),
428
		'pagenum' => 'int',
429
	);
430
	foreach ( $args as $var => $vals ) {
431
		if ( !isset( $_REQUEST[$var] ) )
432
			continue;
433
		if ( is_array( $vals ) ) {
434
			if ( in_array( $_REQUEST[$var], $vals ) )
435
				$q[$var] = $_REQUEST[$var];
436
		} elseif ( $vals == 'int' ) {
437
			$q[$var] = intval( $_REQUEST[$var] );
438
		} elseif ( $vals == 'date' ) {
439
			if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $_REQUEST[$var] ) )
440
				$q[$var] = $_REQUEST[$var];
441
		} 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...
442
			$q[$var] = '';
443
		} elseif ( $vals == 'data' ) {
444
			if ( substr( $_REQUEST[$var], 0, 9 ) == 'index.php' )
445
				$q[$var] = $_REQUEST[$var];
446
		}
447
	}
448
449
	if ( isset( $_GET['chart'] ) ) {
450
		if ( preg_match( '/^[a-z0-9-]+$/', $_GET['chart'] ) ) {
451
			$chart = sanitize_title( $_GET['chart'] );
452
			$url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-includes/charts/{$chart}.php";
453
		}
454
	} else {
455
		$url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-admin/index.php";
456
	}
457
458
	$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...
459
	$method = 'GET';
460
	$timeout = 90;
461
	$user_id = JETPACK_MASTER_USER; // means send the wp.com user_id
462
463
	$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
464
	$get_code = wp_remote_retrieve_response_code( $get );
465
	if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
466
		stats_print_wp_remote_error( $get, $url );
467
	} else {
468
		if ( !empty( $get['headers']['content-type'] ) ) {
469
			$type = $get['headers']['content-type'];
470
			if ( substr( $type, 0, 5 ) == 'image' ) {
471
				$img = $get['body'];
472
				header( 'Content-Type: ' . $type );
473
				header( 'Content-Length: ' . strlen( $img ) );
474
				echo $img;
475
				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...
476
			}
477
		}
478
		$body = stats_convert_post_titles( $get['body'] );
479
		$body = stats_convert_chart_urls( $body );
480
		$body = stats_convert_image_urls( $body );
481
		$body = stats_convert_admin_urls( $body );
482
		echo $body;
483
	}
484
	if ( isset( $_GET['noheader'] ) )
485
		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...
486
}
487
488
function stats_convert_admin_urls( $html ) {
489
	return str_replace( 'index.php?page=stats', 'admin.php?page=stats', $html );
490
}
491
492
function stats_convert_image_urls( $html ) {
493
	$url = set_url_scheme( 'https://' . STATS_DASHBOARD_SERVER );
494
	$html = preg_replace( '|(["\'])(/i/stats.+)\\1|', '$1' . $url . '$2$1', $html );
495
	return $html;
496
}
497
498
function stats_convert_chart_urls( $html ) {
499
	$html = preg_replace_callback( '|https?://[-.a-z0-9]+/wp-includes/charts/([-.a-z0-9]+).php(\??)|',
500
			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...
501
				'$matches',
502
				// If there is a query string, change the beginning '?' to a '&' so it fits into the middle of this query string
503
				'return "admin.php?page=stats&noheader&chart=" . $matches[1] . str_replace( "?", "&", $matches[2] );'
504
			),
505
			$html );
506
	return $html;
507
}
508
509
function stats_convert_post_titles( $html ) {
510
	global $wpdb, $stats_posts;
511
	$pattern = "<span class='post-(\d+)-link'>.*?</span>";
512
	if ( !preg_match_all( "!$pattern!", $html, $matches ) )
513
		return $html;
514
	$posts = get_posts( array(
515
		'include' => implode( ',', $matches[1] ),
516
		'post_type' => 'any',
517
		'post_status' => 'any',
518
		'numberposts' => -1,
519
	));
520
	foreach ( $posts as $post )
521
		$stats_posts[$post->ID] = $post;
522
	$html = preg_replace_callback( "!$pattern!", 'stats_convert_post_title', $html );
523
	return $html;
524
}
525
526
function stats_convert_post_title( $matches ) {
527
	global $stats_posts;
528
	$post_id = $matches[1];
529
	if ( isset( $stats_posts[$post_id] ) )
530
		return '<a href="' . get_permalink( $post_id ) . '" target="_blank">' . get_the_title( $post_id ) . '</a>';
531
	return $matches[0];
532
}
533
534
function stats_configuration_load() {
535
	if ( isset( $_POST['action'] ) && $_POST['action'] == 'save_options' && $_POST['_wpnonce'] == wp_create_nonce( 'stats' ) ) {
536
		$options = stats_get_options();
537
		$options['admin_bar']  = isset( $_POST['admin_bar']  ) && $_POST['admin_bar'];
538
		$options['hide_smile'] = isset( $_POST['hide_smile'] ) && $_POST['hide_smile'];
539
540
		$options['roles'] = array( 'administrator' );
541 View Code Duplication
		foreach ( get_editable_roles() as $role => $details )
542
			if ( isset( $_POST["role_$role"] ) && $_POST["role_$role"] )
543
				$options['roles'][] = $role;
544
545
		$options['count_roles'] = array();
546 View Code Duplication
		foreach ( get_editable_roles() as $role => $details )
547
			if ( isset( $_POST["count_role_$role"] ) && $_POST["count_role_$role"] )
548
				$options['count_roles'][] = $role;
549
550
		stats_set_options( $options );
551
		stats_update_blog();
552
		Jetpack::state( 'message', 'module_configured' );
553
		wp_safe_redirect( Jetpack::module_configuration_url( 'stats' ) );
554
		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...
555
	}
556
}
557
558
function stats_configuration_head() {
559
	?>
560
	<style type="text/css">
561
		#statserror {
562
			border: 1px solid #766;
563
			background-color: #d22;
564
			padding: 1em 3em;
565
		}
566
		.stats-smiley {
567
			vertical-align: 1px;
568
		}
569
	</style>
570
	<?php
571
}
572
573
function stats_configuration_screen() {
574
	$options = stats_get_options();
575
	?>
576
	<div class="narrow">
577
		<p><?php printf( __( 'Visit <a href="%s">Site Stats</a> to see your stats.', 'jetpack' ), esc_url( menu_page_url( 'stats', false ) ) ); ?></p>
578
		<form method="post">
579
		<input type='hidden' name='action' value='save_options' />
580
		<?php wp_nonce_field( 'stats' ); ?>
581
		<table id="menu" class="form-table">
582
		<tr valign="top"><th scope="row"><label for="admin_bar"><?php _e( 'Admin bar' , 'jetpack' ); ?></label></th>
583
		<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>
584
		<tr valign="top"><th scope="row"><?php _e( 'Registered users', 'jetpack' ); ?></th>
585
		<td>
586
			<?php _e( "Count the page views of registered users who are logged in.", 'jetpack' ); ?><br/>
587
			<?php
588
			$count_roles = stats_get_option( 'count_roles' );
589
			foreach ( get_editable_roles() as $role => $details ) {
590
				?>
591
				<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/>
592
				<?php
593
			}
594
			?>
595
		</td></tr>
596
		<tr valign="top"><th scope="row"><?php _e( 'Smiley' , 'jetpack' ); ?></th>
597
		<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>
598
		<tr valign="top"><th scope="row"><?php _e( 'Report visibility' , 'jetpack' ); ?></th>
599
		<td>
600
			<?php _e( 'Select the roles that will be able to view stats reports.', 'jetpack' ); ?><br/>
601
			<?php
602
			$stats_roles = stats_get_option( 'roles' );
603
			foreach ( get_editable_roles() as $role => $details ) {
604
				?>
605
				<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/>
606
				<?php
607
			}
608
			?>
609
		</td></tr>
610
		</table>
611
		<p class="submit"><input type='submit' class='button-primary' value='<?php echo esc_attr( __( 'Save configuration', 'jetpack' ) ); ?>' /></p>
612
		</form>
613
	</div>
614
	<?php
615
}
616
617
function stats_hide_smile_css() {
618
	$options = stats_get_options();
619
	if ( isset( $options['hide_smile'] ) && $options['hide_smile'] ) {
620
	?>
621
<style type='text/css'>img#wpstats{display:none}</style><?php
622
	}
623
}
624
625
function stats_admin_bar_head() {
626
	if ( !stats_get_option( 'admin_bar' ) )
627
		return;
628
629
	if ( !current_user_can( 'view_stats' ) )
630
		return;
631
632
	if ( function_exists( 'is_admin_bar_showing' ) && !is_admin_bar_showing() ) {
633
		return;
634
	}
635
636
	add_action( 'admin_bar_menu', 'stats_admin_bar_menu', 100 );
637
	?>
638
639
<style type='text/css'>
640
#wpadminbar .quicklinks li#wp-admin-bar-stats {
641
	height: 28px;
642
}
643
#wpadminbar .quicklinks li#wp-admin-bar-stats a {
644
	height: 28px;
645
	padding: 0;
646
}
647
#wpadminbar .quicklinks li#wp-admin-bar-stats a div {
648
	height: 28px;
649
	width: 95px;
650
	overflow: hidden;
651
	margin: 0 10px;
652
}
653
#wpadminbar .quicklinks li#wp-admin-bar-stats a:hover div {
654
	width: auto;
655
	margin: 0 8px 0 10px;
656
}
657
#wpadminbar .quicklinks li#wp-admin-bar-stats a img {
658
	height: 24px;
659
	padding: 2px 0;
660
	max-width: none;
661
	border: none;
662
}
663
</style>
664
<?php
665
}
666
667
function stats_admin_bar_menu( &$wp_admin_bar ) {
668
	$url = add_query_arg( 'page', 'stats', admin_url( 'admin.php' ) ); // no menu_page_url() blog-side.
669
670
	$img_src = esc_attr( add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours-scale' ), $url ) );
671
	$img_src_2x = esc_attr( add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours-scale-2x' ), $url ) );
672
673
	$alt = esc_attr( __( 'Stats', 'jetpack' ) );
674
675
	$title = esc_attr( __( 'Views over 48 hours. Click for more Site Stats.', 'jetpack' ) );
676
677
	$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 );
678
679
	$wp_admin_bar->add_menu( $menu );
680
}
681
682
function stats_update_blog() {
683
	Jetpack::xmlrpc_async_call( 'jetpack.updateBlog', stats_get_blog() );
684
}
685
686
function stats_get_blog() {
687
	$home = parse_url( trailingslashit( get_option( 'home' ) ) );
688
	$blog = array(
689
		'host'                => $home['host'],
690
		'path'                => $home['path'],
691
		'blogname'            => get_option( 'blogname' ),
692
		'blogdescription'     => get_option( 'blogdescription' ),
693
		'siteurl'             => get_option( 'siteurl' ),
694
		'gmt_offset'          => get_option( 'gmt_offset' ),
695
		'timezone_string'     => get_option( 'timezone_string' ),
696
		'stats_version'       => STATS_VERSION,
697
		'stats_api'           => 'jetpack',
698
		'page_on_front'       => get_option( 'page_on_front' ),
699
		'permalink_structure' => get_option( 'permalink_structure' ),
700
		'category_base'       => get_option( 'category_base' ),
701
		'tag_base'            => get_option( 'tag_base' ),
702
	);
703
	$blog = array_merge( stats_get_options(), $blog );
704
	unset( $blog['roles'], $blog['blog_id'] );
705
	return stats_esc_html_deep( $blog );
706
}
707
708
/**
709
 * Modified from stripslashes_deep()
710
 */
711
function stats_esc_html_deep( $value ) {
712
	if ( is_array( $value ) ) {
713
		$value = array_map( 'stats_esc_html_deep', $value );
714
	} elseif ( is_object( $value ) ) {
715
		$vars = get_object_vars( $value );
716
		foreach ( $vars as $key => $data ) {
717
			$value->{$key} = stats_esc_html_deep( $data );
718
		}
719
	} elseif ( is_string( $value ) ) {
720
		$value = esc_html( $value );
721
	}
722
723
	return $value;
724
}
725
726
function stats_xmlrpc_methods( $methods ) {
727
	$my_methods = array(
728
		'jetpack.getBlog' => 'stats_get_blog',
729
	);
730
731
	return array_merge( $methods, $my_methods );
732
}
733
734
function stats_register_dashboard_widget() {
735
	if ( ! current_user_can( 'view_stats' ) )
736
		return;
737
738
	// wp_dashboard_empty: we load in the content after the page load via JS
739
	wp_add_dashboard_widget( 'dashboard_stats', __( 'Site Stats', 'jetpack' ), 'wp_dashboard_empty', 'stats_dashboard_widget_control' );
740
741
	add_action( 'admin_head', 'stats_dashboard_head' );
742
}
743
744
function stats_dashboard_widget_options() {
745
	$defaults = array( 'chart' => 1, 'top' => 1, 'search' => 7 );
746
	if ( ( !$options = get_option( 'stats_dashboard_widget' ) ) || !is_array( $options ) )
747
		$options = array();
748
749
	// Ignore obsolete option values
750
	$intervals = array( 1, 7, 31, 90, 365 );
751
	foreach ( array( 'top', 'search' ) as $key )
752
		if ( isset( $options[$key] ) && !in_array( $options[$key], $intervals ) )
753
			unset( $options[$key] );
754
755
	return array_merge( $defaults, $options );
756
}
757
758
function stats_dashboard_widget_control() {
759
	$periods   = array(
760
		'1' => __( 'day', 'jetpack' ),
761
		'7' => __( 'week', 'jetpack' ),
762
		'31' => __( 'month', 'jetpack' ),
763
	);
764
	$intervals = array(
765
		'1' => __( 'the past day', 'jetpack' ),
766
		'7' => __( 'the past week', 'jetpack' ),
767
		'31' => __( 'the past month', 'jetpack' ),
768
		'90' => __( 'the past quarter', 'jetpack' ),
769
		'365' => __( 'the past year', 'jetpack' ),
770
	);
771
	$defaults = array(
772
		'top' => 1,
773
		'search' => 7,
774
	);
775
776
	$options = stats_dashboard_widget_options();
777
778
	if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && isset( $_POST['widget_id'] ) && 'dashboard_stats' == $_POST['widget_id'] ) {
779
		if ( isset( $periods[ $_POST['chart'] ] ) )
780
			$options['chart'] = $_POST['chart'];
781
		foreach ( array( 'top', 'search' ) as $key ) {
782
			if ( isset( $intervals[ $_POST[$key] ] ) )
783
				$options[$key] = $_POST[$key];
784
			else
785
				$options[$key] = $defaults[$key];
786
		}
787
		update_option( 'stats_dashboard_widget', $options );
788
	}
789
	?>
790
	<p>
791
	<label for="chart"><?php _e( 'Chart stats by' , 'jetpack' ); ?></label>
792
	<select id="chart" name="chart">
793
	<?php
794
	foreach ( $periods as $val => $label ) {
795
		?>
796
		<option value="<?php echo $val; ?>"<?php selected( $val, $options['chart'] ); ?>><?php echo esc_html( $label ); ?></option>
797
		<?php
798
	}
799
	?>
800
	</select>.
801
	</p>
802
803
	<p>
804
	<label for="top"><?php _e( 'Show top posts over', 'jetpack' ); ?></label>
805
	<select id="top" name="top">
806
	<?php
807 View Code Duplication
	foreach ( $intervals as $val => $label ) {
808
		?>
809
		<option value="<?php echo $val; ?>"<?php selected( $val, $options['top'] ); ?>><?php echo esc_html( $label ); ?></option>
810
		<?php
811
	}
812
	?>
813
	</select>.
814
	</p>
815
816
	<p>
817
	<label for="search"><?php _e( 'Show top search terms over', 'jetpack' ); ?></label>
818
	<select id="search" name="search">
819
	<?php
820 View Code Duplication
	foreach ( $intervals as $val => $label ) {
821
		?>
822
		<option value="<?php echo $val; ?>"<?php selected( $val, $options['search'] ); ?>><?php echo esc_html( $label ); ?></option>
823
		<?php
824
	}
825
	?>
826
	</select>.
827
	</p>
828
	<?php
829
}
830
831
function stats_jetpack_dashboard_widget() {
832
	?>
833
	<form id="stats_dashboard_widget_control" action="<?php echo esc_url( admin_url() ); ?>" method="post">
834
		<?php stats_dashboard_widget_control(); ?>
835
		<?php wp_nonce_field( 'edit-dashboard-widget_dashboard_stats', 'dashboard-widget-nonce' ); ?>
836
		<input type="hidden" name="widget_id" value="dashboard_stats" />
837
		<?php submit_button( __( 'Submit', 'jetpack' ) ); ?>
838
	</form>
839
	<span id="js-toggle-stats_dashboard_widget_control">
840
		<?php esc_html_e( 'Configure', 'jetpack' ); ?>
841
	</span>
842
	<div id="dashboard_stats">
843
		<div class="inside">
844
			<div style="height: 250px;"></div>
845
		</div>
846
	</div>
847
	<script>
848
		jQuery(document).ready(function($){
849
			var $toggle = $('#js-toggle-stats_dashboard_widget_control');
850
851
			$toggle.parent().prev().append( $toggle );
852
			$toggle.show().click(function(e){
853
				e.preventDefault();
854
				e.stopImmediatePropagation();
855
				$(this).parent().toggleClass('controlVisible');
856
				$('#stats_dashboard_widget_control').slideToggle();
857
			});
858
		});
859
	</script>
860
	<style>
861
		#js-toggle-stats_dashboard_widget_control {
862
			display: none;
863
			float: right;
864
			margin-top: 0.2em;
865
			font-weight: 400;
866
			color: #444;
867
			font-size: .8em;
868
			text-decoration: underline;
869
			cursor: pointer;
870
		}
871
		#stats_dashboard_widget_control {
872
			display: none;
873
			padding: 0 10px;
874
			overflow: hidden;
875
		}
876
		#stats_dashboard_widget_control .button-primary {
877
			float: right;
878
		}
879
		#dashboard_stats {
880
			box-sizing: border-box;
881
			width: 100%;
882
			padding: 0 10px;
883
		}
884
	</style>
885
	<?php
886
}
887
888
function stats_register_widget_control_callback() {
889
	$GLOBALS['wp_dashboard_control_callbacks']['dashboard_stats'] = 'stats_dashboard_widget_control';
890
}
891
// JavaScript and CSS for dashboard widget
892
function stats_dashboard_head() { ?>
893
<script type="text/javascript">
894
/* <![CDATA[ */
895
jQuery( function($) {
896
	var dashStats = jQuery( '#dashboard_stats div.inside' );
897
898
	if ( dashStats.find( '.dashboard-widget-control-form' ).length ) {
899
		return;
900
	}
901
902
	if ( ! dashStats.length ) {
903
		dashStats = jQuery( '#dashboard_stats div.dashboard-widget-content' );
904
		var h = parseInt( dashStats.parent().height() ) - parseInt( dashStats.prev().height() );
905
		var args = 'width=' + dashStats.width() + '&height=' + h.toString();
906
	} else {
907
		if ( jQuery('#dashboard_stats' ).hasClass('postbox') ) {
908
			var args = 'width=' + ( dashStats.prev().width() * 2 ).toString();
909
		} else {
910
			var args = 'width=' + ( dashStats.width() * 2 ).toString();
911
		}
912
	}
913
914
	dashStats
915
		.not( '.dashboard-widget-control' )
916
		.load( 'admin.php?page=stats&noheader&dashboard&' + args );
917
918
	jQuery( window ).one( 'resize', function() {
919
		jQuery( '#stat-chart' ).css( 'width', 'auto' );
920
	} );
921
} );
922
/* ]]> */
923
</script>
924
<style type="text/css">
925
/* <![CDATA[ */
926
#stat-chart {
927
	background: none !important;
928
}
929
#dashboard_stats .inside {
930
	margin: 10px 0 0 0 !important;
931
}
932
#dashboard_stats #stats-graph {
933
	margin: 0;
934
}
935
#stats-info {
936
	border-top: 1px solid #dfdfdf;
937
	margin: 7px -10px 0 -10px;
938
	padding: 10px;
939
	background: #fcfcfc;
940
	-moz-box-shadow:inset 0 1px 0 #fff;
941
	-webkit-box-shadow:inset 0 1px 0 #fff;
942
	box-shadow:inset 0 1px 0 #fff;
943
	overflow: hidden;
944
	border-radius: 0 0 2px 2px;
945
	-webkit-border-radius: 0 0 2px 2px;
946
	-moz-border-radius: 0 0 2px 2px;
947
	-khtml-border-radius: 0 0 2px 2px;
948
}
949
#stats-info #top-posts, #stats-info #top-search {
950
	float: left;
951
	width: 50%;
952
}
953
#top-posts .stats-section-inner p {
954
	white-space: nowrap;
955
	overflow: hidden;
956
}
957
#top-posts .stats-section-inner p a {
958
	overflow: hidden;
959
	text-overflow: ellipsis;
960
}
961
#stats-info div#active {
962
	border-top: 1px solid #dfdfdf;
963
	margin: 0 -10px;
964
	padding: 10px 10px 0 10px;
965
	-moz-box-shadow:inset 0 1px 0 #fff;
966
	-webkit-box-shadow:inset 0 1px 0 #fff;
967
	box-shadow:inset 0 1px 0 #fff;
968
	overflow: hidden;
969
}
970
#top-search p {
971
	color: #999;
972
}
973
#stats-info h3 {
974
	font-size: 1em;
975
	margin: 0 0 .5em 0 !important;
976
}
977
#stats-info p {
978
	margin: 0 0 .25em;
979
	color: #999;
980
}
981
#stats-info p.widget-loading {
982
	margin: 1em 0 0;
983
	color: #333;
984
}
985
#stats-info p a {
986
	display: block;
987
}
988
#stats-info p a.button {
989
	display: inline;
990
}
991
/* ]]> */
992
</style>
993
<?php
994
}
995
996
function stats_dashboard_widget_content() {
997
	if ( !isset( $_GET['width'] ) || ( !$width  = (int) ( $_GET['width'] / 2 ) ) || $width  < 250 )
998
		$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...
999
	if ( !isset( $_GET['height'] ) || ( !$height = (int) $_GET['height'] - 36 )   || $height < 230 )
1000
		$height = 180;
1001
1002
	$_width  = $width  - 5;
1003
	$_height = $height - ( $GLOBALS['is_winIE'] ? 16 : 5 ); // hack!
1004
1005
	$options = stats_dashboard_widget_options();
1006
	$blog_id = Jetpack_Options::get_option( 'id' );
1007
1008
	$q = array(
1009
		'noheader' => 'true',
1010
		'proxy' => '',
1011
		'blog' => $blog_id,
1012
		'page' => 'stats',
1013
		'chart' => '',
1014
		'unit' => $options['chart'],
1015
		'color' => get_user_option( 'admin_color' ),
1016
		'width' => $_width,
1017
		'height' => $_height,
1018
		'ssl' => is_ssl(),
1019
		'j' => sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ),
1020
	);
1021
1022
	$url = 'https://' . STATS_DASHBOARD_SERVER . "/wp-admin/index.php";
1023
1024
	$url = add_query_arg( $q, $url );
1025
	$method = 'GET';
1026
	$timeout = 90;
1027
	$user_id = JETPACK_MASTER_USER;
1028
1029
	$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1030
	$get_code = wp_remote_retrieve_response_code( $get );
1031
	if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
1032
		stats_print_wp_remote_error( $get, $url );
1033
	} else {
1034
		$body = stats_convert_post_titles($get['body']);
1035
		$body = stats_convert_chart_urls($body);
1036
		$body = stats_convert_image_urls($body);
1037
		echo $body;
1038
	}
1039
1040
	$post_ids = array();
1041
1042
	$csv_end_date = date( 'Y-m-d', current_time( 'timestamp' ) );
1043
	$csv_args = array( 'top' => "&limit=8&end=$csv_end_date", 'search' => "&limit=5&end=$csv_end_date" );
1044
	/* translators: Stats dashboard widget postviews list: "$post_title $views Views" */
1045
	$printf = __( '%1$s %2$s Views' , 'jetpack' );
1046
1047
	foreach ( $top_posts = stats_get_csv( 'postviews', "days=$options[top]$csv_args[top]" ) as $i => $post ) {
1048
		if ( $post['post_id'] == 0 ) {
1049
			unset( $top_posts[$i] );
1050
			continue;
1051
		}
1052
		$post_ids[] = $post['post_id'];
1053
	}
1054
1055
	// cache
1056
	get_posts( array( 'include' => join( ',', array_unique( $post_ids ) ) ) );
1057
1058
	$searches = array();
1059
	foreach ( $search_terms = stats_get_csv( 'searchterms', "days=$options[search]$csv_args[search]" ) as $search_term ) {
1060
		if ( $search_term['searchterm'] == 'encrypted_search_terms' )
1061
			continue;
1062
		$searches[] = esc_html( $search_term['searchterm'] );
1063
	}
1064
1065
?>
1066
<a class="button" href="admin.php?page=stats"><?php _e( 'View All', 'jetpack' ); ?></a>
1067
<div id="stats-info">
1068
	<div id="top-posts" class='stats-section'>
1069
		<div class="stats-section-inner">
1070
		<h3 class="heading"><?php _e( 'Top Posts' , 'jetpack' ); ?></h3>
1071
		<?php
1072
		if ( empty( $top_posts ) ) {
1073
			?>
1074
			<p class="nothing"><?php _e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1075
			<?php
1076
		} else {
1077
			foreach ( $top_posts as $post ) {
1078
				if ( !get_post( $post['post_id'] ) )
1079
					continue;
1080
				?>
1081
				<p><?php printf(
1082
					$printf,
1083
					'<a href="' . get_permalink( $post['post_id'] ) . '">' . get_the_title( $post['post_id'] ) . '</a>',
1084
					number_format_i18n( $post['views'] )
1085
				); ?></p>
1086
				<?php
1087
			}
1088
		}
1089
		?>
1090
		</div>
1091
	</div>
1092
	<div id="top-search" class='stats-section'>
1093
		<div class="stats-section-inner">
1094
		<h3 class="heading"><?php _e( 'Top Searches' , 'jetpack' ); ?></h3>
1095
		<?php
1096
		if ( empty( $searches ) ) {
1097
			?>
1098
			<p class="nothing"><?php _e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
1099
			<?php
1100
		} else {
1101
			?>
1102
			<p><?php echo join( ',&nbsp; ', $searches );?></p>
1103
			<?php
1104
		}
1105
		?>
1106
		</div>
1107
	</div>
1108
</div>
1109
<div class="clear"></div>
1110
<?php
1111
	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...
1112
}
1113
1114
function stats_print_wp_remote_error( $get, $url ) {
1115
	$state_name = 'stats_remote_error_' . substr( md5( $url ), 0, 8 );
1116
	$previous_error = Jetpack::state( $state_name );
1117
	$error = md5( serialize( compact( 'get', 'url' ) ) );
1118
	Jetpack::state( $state_name, $error );
1119
	if ( $error !== $previous_error ) {
1120
?>
1121
	<div class="wrap">
1122
	<p><?php _e( 'We were unable to get your stats just now. Please reload this page to try again.', 'jetpack' ); ?></p>
1123
	</div>
1124
<?php
1125
		return;
1126
	}
1127
?>
1128
	<div class="wrap">
1129
	<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>
1130
	<pre>
1131
	User Agent: "<?php echo esc_html( $_SERVER['HTTP_USER_AGENT'] ); ?>"
1132
	Page URL: "http<?php echo (is_ssl()?'s':'') . '://' . esc_html( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); ?>"
1133
	API URL: "<?php echo esc_url( $url ); ?>"
1134
<?php
1135
	if ( is_wp_error( $get ) ) {
1136
		foreach ( $get->get_error_codes() as $code ) {
1137
			foreach ( $get->get_error_messages($code) as $message ) {
1138
				?>
1139
	<?php print $code . ': "' . $message . '"' ?>
1140
1141
<?php
1142
			}
1143
		}
1144
	} else {
1145
		$get_code = wp_remote_retrieve_response_code( $get );
1146
		$content_length = strlen( wp_remote_retrieve_body( $get ) );
1147
		?>
1148
	Response code: "<?php print $get_code ?>"
1149
	Content length: "<?php print $content_length ?>"
1150
1151
<?php
1152
	}
1153
	?></pre>
1154
	</div>
1155
	<?php
1156
}
1157
1158
/**
1159
 * Get stats from WordPress.com
1160
 *
1161
 * @param string $table The stats which you want to retrieve: postviews, or searchterms
1162
 * @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...
1163
 *     An associative array of arguments.
1164
 *
1165
 *      @type bool    $end        The last day of the desired time frame. Format is 'Y-m-d' (e.g. 2007-05-01)
1166
 *                                and default timezone is UTC date. Default value is Now.
1167
 *      @type string  $days       The length of the desired time frame. Default is 30. Maximum 90 days.
1168
 *      @type int     $limit      The maximum number of records to return. Default is 10. Maximum 100.
1169
 *      @type int     $post_id    The ID of the post to retrieve stats data for
1170
 *      @type string  $summarize  If present, summarizes all matching records. Default Null.
1171
 *
1172
 * }
1173
 *
1174
 * @return array {
1175
 *      An array of post view data, each post as an array
1176
 *
1177
 *      array {
1178
 *          The post view data for a single post
1179
 *
1180
 *          @type string  $post_id         The ID of the post
1181
 *          @type string  $post_title      The title of the post
1182
 *          @type string  $post_permalink  The permalink for the post
1183
 *          @type string  $views           The number of views for the post within the $num_days specified
1184
 *      }
1185
 * }
1186
 */
1187
function stats_get_csv( $table, $args = null ) {
1188
	$defaults = array( 'end' => false, 'days' => false, 'limit' => 3, 'post_id' => false, 'summarize' => '' );
1189
1190
	$args = wp_parse_args( $args, $defaults );
1191
	$args['table'] = $table;
1192
	$args['blog_id'] = Jetpack_Options::get_option( 'id' );
1193
1194
	$stats_csv_url = add_query_arg( $args, 'http://stats.wordpress.com/csv.php' );
1195
1196
	$key = md5( $stats_csv_url );
1197
1198
	// Get cache
1199
	$stats_cache = get_option( 'stats_cache' );
1200
	if ( !$stats_cache || !is_array( $stats_cache ) )
1201
		$stats_cache = array();
1202
1203
	// Return or expire this key
1204
	if ( isset( $stats_cache[$key] ) ) {
1205
		$time = key( $stats_cache[$key] );
1206
		if ( time() - $time < 300 )
1207
			return $stats_cache[$key][$time];
1208
		unset( $stats_cache[$key] );
1209
	}
1210
1211
	$stats_rows = array();
1212
	do {
1213
		if ( !$stats = stats_get_remote_csv( $stats_csv_url ) )
1214
			break;
1215
1216
		$labels = array_shift( $stats );
1217
1218
		if ( 0 === stripos( $labels[0], 'error' ) )
1219
			break;
1220
1221
		$stats_rows = array();
1222
		for ( $s = 0; isset( $stats[$s] ); $s++ ) {
1223
			$row = array();
1224
			foreach ( $labels as $col => $label )
1225
				$row[$label] = $stats[$s][$col];
1226
			$stats_rows[] = $row;
1227
		}
1228
	} while( 0 );
1229
1230
	// Expire old keys
1231 View Code Duplication
	foreach ( $stats_cache as $k => $cache )
1232
		if ( !is_array( $cache ) || 300 < time() - key($cache) )
1233
			unset( $stats_cache[$k] );
1234
1235
	// Set cache
1236
	$stats_cache[$key] = array( time() => $stats_rows );
1237
	update_option( 'stats_cache', $stats_cache );
1238
1239
	return $stats_rows;
1240
}
1241
1242
function stats_get_remote_csv( $url ) {
1243
	$method = 'GET';
1244
	$timeout = 90;
1245
	$user_id = JETPACK_MASTER_USER;
1246
1247
	$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
1248
	$get_code = wp_remote_retrieve_response_code( $get );
1249
	if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
1250
		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...
1251
	} else {
1252
		return stats_str_getcsv( $get['body'] );
1253
	}
1254
}
1255
1256
// rather than parsing the csv and its special cases, we create a new file and do fgetcsv on it.
1257
function stats_str_getcsv( $csv ) {
1258
	if ( function_exists( 'str_getcsv' ) ) {
1259
		$lines = str_getcsv( $csv, "\n" );
1260
		return array_map( 'str_getcsv', $lines );
1261
	}
1262
	if ( !$temp = tmpfile() ) // tmpfile() automatically unlinks
1263
		return false;
1264
1265
	$data = array();
1266
1267
	fwrite( $temp, $csv, strlen( $csv ) );
1268
	fseek( $temp, 0 );
1269
	while ( false !== $row = fgetcsv( $temp, 2000 ) )
1270
		$data[] = $row;
1271
	fclose( $temp );
1272
1273
	return $data;
1274
}
1275
1276
/**
1277
 * Abstract out building the rest api stats path.
1278
 *
1279
 * @param  string $resource
1280
 * @return string
1281
 */
1282
function jetpack_stats_api_path( $resource = '' ) {
1283
	$resource = ltrim( $resource, '/' );
1284
	return sprintf( '/sites/%d/stats/%s', stats_get_option( 'blog_id' ), $resource );
1285
}
1286
1287
/**
1288
 * Fetches stats data from the REST API.  Caches locally for 5 minutes.
1289
 *
1290
 * @link: https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/stats/
1291
 *
1292
 * @param  array|string   $args     The args that are passed to the endpoint
1293
 * @param  string         $resource Optional sub-endpoint following /stats/
1294
 * @return array|WP_Error
1295
 */
1296
function stats_get_from_restapi( $args = array(), $resource = '' ) {
1297
	$endpoint    = jetpack_stats_api_path( $resource );
1298
	$api_version = '1.1';
1299
	$args        = wp_parse_args( $args, array() );
1300
	$cache_key   = md5( implode( '|', array( $endpoint, $api_version, serialize( $args ) ) ) );
1301
1302
	// Get cache
1303
	$stats_cache = Jetpack_Options::get_option( 'restapi_stats_cache', array() );
1304
	if ( ! is_array( $stats_cache ) ) {
1305
		$stats_cache = array();
1306
	}
1307
1308
	// Return or expire this key
1309
	if ( isset( $stats_cache[ $cache_key ] ) ) {
1310
		$time = key( $stats_cache[ $cache_key ] );
1311
		if ( time() - $time < ( 5 * MINUTE_IN_SECONDS ) ) {
1312
			$cached_stats = $stats_cache[ $cache_key ][ $time ];
1313
			$cached_stats = (object) array_merge( array( 'cached_at' => $time ), (array) $cached_stats );
1314
			return $cached_stats;
1315
		}
1316
		unset( $stats_cache[ $cache_key ] );
1317
	}
1318
1319
	// Do the dirty work.
1320
	$response = Jetpack_Client::wpcom_json_api_request_as_blog( $endpoint, $api_version, $args );
1321
	if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
1322
		// If bad, just return it, don't cache.
1323
		return $response;
1324
	}
1325
1326
	$data = json_decode( wp_remote_retrieve_body( $response ) );
1327
1328
	// Expire old keys
1329 View Code Duplication
	foreach ( $stats_cache as $k => $cache ) {
1330
		if ( ! is_array( $cache ) || ( 5 * MINUTE_IN_SECONDS ) < time() - key( $cache ) ) {
1331
			unset( $stats_cache[ $k ] );
1332
		}
1333
	}
1334
1335
	// Set cache
1336
	$stats_cache[ $cache_key ] = array(
1337
		time() => $data,
1338
	);
1339
	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...
1340
1341
	return $data;
1342
}
1343