Completed
Push — add/about-page ( 67334a...6eb58f )
by
unknown
07:37 queued 57s
created

Jetpack_About_Page   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 435
Duplicated Lines 11.26 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 0
Metric Value
dl 49
loc 435
rs 8.8798
c 0
b 0
f 0
wmc 44
lcom 0
cbo 1

7 Methods

Rating   Name   Duplication   Size   Complexity  
A get_page_hook() 0 11 1
A add_page_actions() 0 1 1
A page_admin_scripts() 0 7 1
A additional_styles() 0 3 1
A render() 0 3 1
B page_render() 0 85 1
F display_plugins() 49 299 38

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_About_Page often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_About_Page, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Disable direct access and execution.
4
 */
5
if ( ! defined( 'ABSPATH' ) ) {
6
	exit;
7
}
8
9
include_once( 'class.jetpack-admin-page.php' );
10
11
// Builds the landing page and its menu
12
class Jetpack_About_Page extends Jetpack_Admin_Page {
13
14
	// Show the settings page only when Jetpack is connected or in dev mode
15
	protected $dont_show_if_not_active = true;
16
17
	/**
18
	 * Add a submenu item to the Jetpack admin menu.
19
	 *
20
	 * @return string
21
	 */
22
	function get_page_hook() {
23
		// Add the main admin Jetpack menu
24
		return add_submenu_page(
25
			'jetpack',
26
			esc_html__( 'About Jetpack', 'jetpack' ),
27
			esc_html__( 'About Jetpack', 'jetpack' ),
28
			'jetpack_admin_page',
29
			'jetpack_about',
30
			array( $this, 'render' )
31
		);
32
	}
33
34
	function add_page_actions( $hook ) {}
35
	function page_admin_scripts() {
36
		wp_enqueue_style( 'plugin-install' );
37
		wp_enqueue_script( 'plugin-install' );
38
39
		wp_enqueue_style( 'thickbox' );
40
		wp_enqueue_script( 'thickbox' );
41
	}
42
43
	/**
44
	 * Load styles for static page.
45
	 */
46
	function additional_styles() {
47
		Jetpack_Admin_Page::load_wrapper_styles();
48
	}
49
50
	/**
51
	 * Render the page with a common top and bottom part, and page specific content
52
	 */
53
	function render() {
54
		Jetpack_Admin_Page::wrap_ui( array( $this, 'page_render' ), array( 'show-nav' => false ) );
55
	}
56
57
	/**
58
	 * Render the page content
59
	 */
60
	function page_render() {
61
		?>
62
		<div class="page-content configure">
63
			<div class="frame top">
64
				<div class="jetpack-about__link-back">
65
					<a href="<?php echo esc_url( admin_url( 'admin.php?page=jetpack' ) ); ?>">
66
						<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="0" fill="none" width="24" height="24"/><g><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></g></svg>
67
						<?php esc_html_e( 'Back to Jetpack Dashboard', 'jetpack' ); ?>
68
					</a>
69
				</div>
70
				<div class="jetpack-about__main">
71
					<div class="jetpack-about__logo">
72
						<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
73
							 viewBox="0 0 800 96" style="enable-background:new 0 0 800 96;" xml:space="preserve">
74
						<g>
75
							<path style="fill: #39c;" d="M292.922,78c-19.777,0-32.598-14.245-32.598-29.078V47.08c0-15.086,12.821-29.08,32.598-29.08
76
								c19.861,0,32.682,13.994,32.682,29.08v1.843C325.604,63.755,312.783,78,292.922,78z M315.044,47.245
77
								c0-10.808-7.877-20.447-22.122-20.447s-22.04,9.639-22.04,20.447v1.341c0,10.811,7.795,20.614,22.04,20.614
78
								s22.122-9.803,22.122-20.614V47.245z"/>
79
							<path d="M69.602,75.821l-7.374-13.826H29.463l-7.124,13.826H11.277l30.167-55.81h8.715l30.671,55.81H69.602z M45.552,30.906
80
								L33.401,54.369h24.72L45.552,30.906z"/>
81
							<path d="M128.427,78c-20.028,0-29.329-10.894-29.329-25.391V20.012h10.391v32.765c0,10.308,6.788,16.424,19.692,16.424
82
								c13.242,0,18.687-6.116,18.687-16.424V20.012h10.475v32.598C158.342,66.436,149.46,78,128.427,78z"/>
83
							<path d="M216.667,28.727v47.094h-10.475V28.727h-24.386v-8.715h59.245v8.715H216.667z"/>
84
							<path d="M418.955,75.821V31.659l-2.766,4.861l-23.379,39.301h-5.112L364.569,36.52l-2.765-4.861v44.162h-10.224v-55.81h14.497
85
								l22.038,38.296L390.713,63l2.599-4.692l21.786-38.296h14.331v55.81H418.955z"/>
86
							<path d="M508.619,75.821l-7.374-13.826H468.48l-7.123,13.826h-11.061l30.167-55.81h8.715l30.669,55.81H508.619z M484.569,30.906
87
								l-12.151,23.464h24.72L484.569,30.906z"/>
88
							<path d="M562.081,28.727v47.094h-10.474V28.727h-24.386v-8.715h59.245v8.715H562.081z"/>
89
							<path d="M638.924,28.727v47.094H628.45V28.727h-24.386v-8.715h59.245v8.715H638.924z"/>
90
							<path d="M689.118,75.821v-50.53c4.19,0,5.866-2.263,5.866-5.28h4.442v55.81H689.118z"/>
91
							<path d="M781.464,35.765c-5.028-4.609-12.402-8.967-22.374-8.967c-14.916,0-23.296,10.225-23.296,20.867v1.089
92
								c0,10.558,8.464,20.445,24.05,20.445c9.303,0,17.012-4.441,21.872-8.965L788,66.854C781.883,72.887,771.492,78,759.174,78
93
								c-21.118,0-33.939-13.743-33.939-28.828v-1.843c0-15.084,13.993-29.329,34.44-29.329c11.816,0,22.541,4.944,28.324,11.146
94
								L781.464,35.765z"/>
95
							<path d="M299.82,37.417c1.889,1.218,2.418,3.749,1.192,5.648l-9.553,14.797c-1.226,1.901-3.752,2.452-5.637,1.234l0,0
96
								c-1.886-1.22-2.421-3.745-1.192-5.647l9.553-14.797C295.41,36.753,297.935,36.201,299.82,37.417L299.82,37.417z"/>
97
						</g>
98
						</svg>
99
					</div>
100
					<div class="jetpack-about__content">
101
						<div class="jetpack-about__images">
102
							<div class="gravatars">[ gravatar block placeholder ]</div>
103
							<a href="https://automattic.com/about/" target="_blank"><?php esc_html_e( 'Meet the team', 'jetpack' ); ?></a>
104
						</div>
105
106
						<div class="jetpack-about__text">
107
							<p>
108
								<?php esc_html_e( 'We are the people behind WordPress.com, WooCommerce, Jetpack, Simplenote, Longreads, VaultPress, Akismet, Gravatar, Crowdsignal, Cloudup, and more. We believe in making the web a better place.', 'jetpack' ); ?>
109
								<a href="https://automattic.com/" target="_blank">
110
									<?php esc_html_e( 'Learn more about us.', 'jetpack' ); ?>
111
								</a>
112
							</p>
113
							<p>
114
								<?php esc_html_e( 'We’re a distributed company with 864 Automatticians in 68 countries speaking 84 different languages. Our common goal is to democratize publishing so that anyone with a story can tell it, regardless of income, gender, politics, language, or where they live in the world.', 'jetpack' ); ?>
115
							</p>
116
							<p>
117
								<?php esc_html_e( 'We believe in Open Source and the vast majority of our work is available under the GPL.', 'jetpack' ); ?>
118
							</p>
119
							<p>
120
								<?php
121
									// Maybe use printf() because we'll want to escape the string but still allow for the link, so we can't use esc_html_e()
122
									echo wp_kses( __( 'We strive to live by the <a href="https://automattic.com/creed/" target="_blank">Automattic Creed</a>.', 'jetpack' ), array( 'a' => array( 'href' => array(), 'target' => array() ) ) ); ?>
123
							</p>
124
							<p>
125
								<a href="https://automattic.com/jobs" target="_blank">
126
									<?php esc_html_e( 'Come work with us', 'jetpack' ); ?>
127
								</a>
128
							</p>
129
						</div>
130
					</div>
131
				</div>
132
133
				<div class="jetpack-about__colophon">
134
					<h3><?php esc_html_e( 'Popular WordPress services by Automattic', 'jetpack' ); ?></h3>
135
					<ul class="jetpack-about__services">
136
					<?php $this->display_plugins(); ?>
137
					</ul>
138
139
					<p class="more"><?php echo wp_kses( __( 'For even more of our WordPress plugins, please <a href="https://profiles.wordpress.org/automattic/" target="_blank">take a look at our WordPress.org profile</a>.', 'jetpack' ), array( 'a' => array( 'href' => array(), 'target' => array() ) ) ); ?></p>
140
				</div>
141
			</div>
142
		</div>
143
		<?php
144
	}
145
146
	function display_plugins(){
147
		$plugins_allowedtags = array(
148
			'a'       => array(
149
				'href'   => array(),
150
				'title'  => array(),
151
				'target' => array(),
152
			),
153
			'abbr'    => array( 'title' => array() ),
154
			'acronym' => array( 'title' => array() ),
155
			'code'    => array(),
156
			'pre'     => array(),
157
			'em'      => array(),
158
			'strong'  => array(),
159
			'ul'      => array(),
160
			'ol'      => array(),
161
			'li'      => array(),
162
			'p'       => array(),
163
			'br'      => array(),
164
		);
165
166
		// slugs for plugins we want to display
167
		$a8c_plugins = array(
168
			'akismet',
169
			'wp-super-cache',
170
			'vaultpress',
171
			'polldaddy',
172
		);
173
174
		// need this to access the plugins_api() function
175
		include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
176
177
		foreach ( $a8c_plugins as $slug ){
178
			$args = array(
179
				'slug'	=> $slug,
180
				'fields'	=> array(
181
					'added'						=> false,
182
					'author'						=> false,
183
					'author_profile'				=> false,
184
					'banners'					=> false,
185
					'contributors'				=> false,
186
					'donate_link'				=> false,
187
					'homepage'					=> false,
188
					'reviews'					=> false,
189
					'screenshots'				=> false,
190
					'support_threads'			=> false,
191
					'support_threads_resolved'	=> false,
192
					'sections'					=> false,
193
					'tags'						=> false,
194
					'versions'					=> false,
195
196
					'compatibility'				=> true,
197
					'downloaded'					=> true,
198
					'downloadlink'				=> true,
199
					'icons'						=> true,
200
					'last_updated'				=> true,
201
					'num_ratings'				=> true,
202
					'rating'						=> true,
203
					'requires'					=> true,
204
					'requires_php'				=> true,
205
					'short_description'			=> true,
206
					'tested'						=> true,
207
				),
208
			);
209
210
			// should probably add some error checking here too
211
			$api = plugins_api( 'plugin_information', $args );
212
			$plugins[] = $api;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$plugins was never initialized. Although not strictly required by PHP, it is generally a good practice to add $plugins = 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...
213
		}
214
215
		foreach ( $plugins as $plugin ) {
0 ignored issues
show
Bug introduced by
The variable $plugins 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...
216
			if ( is_object( $plugin ) ) {
217
				$plugin = (array) $plugin;
218
			}
219
220
			$title = wp_kses( $plugin['name'], $plugins_allowedtags );
221
222
			// Remove any HTML from the description.
223
			$description = strip_tags( $plugin['short_description'] );
224
225
			$wp_version = get_bloginfo( 'version' );
226
227
			$compatible_php = ( empty( $plugin['requires_php'] ) || version_compare( phpversion(), $plugin['requires_php'], '>=' ) );
228
			$tested_wp      = ( empty( $plugin['tested'] ) || version_compare( $wp_version, $plugin['tested'], '<=' ) );
0 ignored issues
show
Unused Code introduced by
$tested_wp is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
229
			$compatible_wp  = ( empty( $plugin['requires'] ) || version_compare( $wp_version, $plugin['requires'], '>=' ) );
230
231
			$action_links = array();
232
233
			// install button
234
			if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) {
235
				$status = install_plugin_install_status( $plugin );
236
				switch ( $status['status'] ) {
237
					case 'install':
238 View Code Duplication
						if ( $status['url'] ) {
239
							if ( $compatible_php && $compatible_wp ) {
240
								$action_links[] = sprintf(
241
									'<a class="install-now button" data-slug="%s" href="%s" aria-label="%s" data-name="%s">%s</a>',
242
									esc_attr( $plugin['slug'] ),
243
									esc_url( $status['url'] ),
244
									/* translators: %s: plugin name and version */
245
									esc_attr( sprintf( __( 'Install %s now' ), $name ) ),
0 ignored issues
show
Bug introduced by
The variable $name does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
246
									esc_attr( $name ),
247
									__( 'Install Now' )
248
								);
249
							} else {
250
								$action_links[] = sprintf(
251
									'<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
252
									_x( 'Cannot Install', 'plugin' )
253
								);
254
							}
255
						}
256
						break;
257
258
					case 'update_available':
259 View Code Duplication
						if ( $status['url'] ) {
260
							$action_links[] = sprintf(
261
								'<a class="update-now button aria-button-if-js" data-plugin="%s" data-slug="%s" href="%s" aria-label="%s" data-name="%s">%s</a>',
262
								esc_attr( $status['file'] ),
263
								esc_attr( $plugin['slug'] ),
264
								esc_url( $status['url'] ),
265
								/* translators: %s: plugin name and version */
266
								esc_attr( sprintf( __( 'Update %s now' ), $name ) ),
267
								esc_attr( $name ),
268
								__( 'Update Now' )
269
							);
270
						}
271
						break;
272
273
					case 'latest_installed':
274
					case 'newer_installed':
275
						if ( is_plugin_active( $status['file'] ) ) {
276
							$action_links[] = sprintf(
277
								'<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
278
								_x( 'Active', 'plugin' )
279
							);
280
						} elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) {
281
							$button_text = __( 'Activate' );
282
							/* translators: %s: plugin name */
283
							$button_label = _x( 'Activate %s', 'plugin' );
284
							$activate_url = add_query_arg(
285
								array(
286
									'_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
287
									'action'   => 'activate',
288
									'plugin'   => $status['file'],
289
								),
290
								network_admin_url( 'plugins.php' )
291
							);
292
293
							if ( is_network_admin() ) {
294
								$button_text = __( 'Network Activate' );
295
								/* translators: %s: plugin name */
296
								$button_label = _x( 'Network Activate %s', 'plugin' );
297
								$activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
298
							}
299
300
							$action_links[] = sprintf(
301
								'<a href="%1$s" class="button activate-now" aria-label="%2$s">%3$s</a>',
302
								esc_url( $activate_url ),
303
								esc_attr( sprintf( $button_label, $plugin['name'] ) ),
304
								$button_text
305
							);
306
						} else {
307
							$action_links[] = sprintf(
308
								'<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
309
								_x( 'Installed', 'plugin' )
310
							);
311
						}
312
						break;
313
				}
314
			}
315
316
			$details_link = self_admin_url(
317
				'plugin-install.php?tab=plugin-information&amp;plugin=' . $plugin['slug'] .
318
				'&amp;TB_iframe=true&amp;width=600&amp;height=550'
319
			);
320
321
			if ( ! empty( $plugin['icons']['svg'] ) ) {
322
				$plugin_icon_url = $plugin['icons']['svg'];
323
			} elseif ( ! empty( $plugin['icons']['2x'] ) ) {
324
				$plugin_icon_url = $plugin['icons']['2x'];
325
			} elseif ( ! empty( $plugin['icons']['1x'] ) ) {
326
				$plugin_icon_url = $plugin['icons']['1x'];
327
			} else {
328
				$plugin_icon_url = $plugin['icons']['default'];
329
			}
330
?>
331
332
		<li class="jetpack-about__plugin plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
333
			<?php
334
			if ( ! $compatible_php || ! $compatible_wp ) {
335
				echo '<div class="notice inline notice-error notice-alt"><p>';
336
				if ( ! $compatible_php && ! $compatible_wp ) {
337
					_e( 'This plugin doesn&#8217;t work with your versions of WordPress and PHP.' );
338
					if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) {
339
						printf(
340
							/* translators: 1: "Update WordPress" screen URL, 2: "Update PHP" page URL */
341
							' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ),
342
							self_admin_url( 'update-core.php' ),
343
							esc_url( wp_get_update_php_url() )
344
						);
345
						wp_update_php_annotation();
346
					} elseif ( current_user_can( 'update_core' ) ) {
347
						printf(
348
							/* translators: %s: "Update WordPress" screen URL */
349
							' ' . __( '<a href="%s">Please update WordPress</a>.' ),
350
							self_admin_url( 'update-core.php' )
351
						);
352 View Code Duplication
					} elseif ( current_user_can( 'update_php' ) ) {
353
						printf(
354
							/* translators: %s: "Update PHP" page URL */
355
							' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
356
							esc_url( wp_get_update_php_url() )
357
						);
358
						wp_update_php_annotation();
359
					}
360
				} elseif ( ! $compatible_wp ) {
361
					_e( 'This plugin doesn&#8217;t work with your version of WordPress.' );
362
					if ( current_user_can( 'update_core' ) ) {
363
						printf(
364
							/* translators: %s: "Update WordPress" screen URL */
365
							' ' . __( '<a href="%s">Please update WordPress</a>.' ),
366
							self_admin_url( 'update-core.php' )
367
						);
368
					}
369 View Code Duplication
				} elseif ( ! $compatible_php ) {
370
					_e( 'This plugin doesn&#8217;t work with your version of PHP.' );
371
					if ( current_user_can( 'update_php' ) ) {
372
						printf(
373
							/* translators: %s: "Update PHP" page URL */
374
							' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ),
375
							esc_url( wp_get_update_php_url() )
376
						);
377
						wp_update_php_annotation();
378
					}
379
				}
380
				echo '</p></div>';
381
			}
382
			?>
383
384
			<div class="plugin-card-top">
385
				<div class="name column-name">
386
					<h3>
387
						<a href="<?php echo esc_url( $details_link ); ?>" class="thickbox open-plugin-details-modal">
388
						<?php echo $title; ?>
389
						<img src="<?php echo esc_attr( $plugin_icon_url ); ?>" class="plugin-icon" alt="">
390
						</a>
391
					</h3>
392
				</div>
393
				<div class="desc column-description">
394
					<p><?php echo $description; ?></p>
395
				</div>
396
397
				<div class="details-link">
398
					<a class="thickbox open-plugin-details-modal" href="<?php echo $details_link; ?>"><?php _e( 'More Details', 'jetpack' ); ?></a>
399
				</div>
400
			</div>
401
402
			<div class="plugin-card-bottom">
403
				<div class="action-links">
404
					<?php
405
					if ( $action_links ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $action_links of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
406
						echo '<ul class="plugin-action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
407
					}
408
					?>
409
				</div>
410
				<div class="vers column-rating">
411
					<?php
412
					wp_star_rating(
413
						array(
414
							'rating' => $plugin['rating'],
415
							'type'   => 'percent',
416
							'number' => $plugin['num_ratings'],
417
						)
418
					);
419
					?>
420
					<span class="num-ratings" aria-hidden="true">(<?php echo number_format_i18n( $plugin['num_ratings'] ); ?> <?php esc_html_e( 'ratings', 'jetpack' ); ?>)</span>
421
				</div>
422
				<div class="column-downloaded">
423
					<?php
424
					if ( $plugin['active_installs'] >= 1000000 ) {
425
						$active_installs_millions = floor( $plugin['active_installs'] / 1000000 );
426
						$active_installs_text     = sprintf(
427
							_nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations' ),
428
							number_format_i18n( $active_installs_millions )
429
						);
430
					} elseif ( 0 == $plugin['active_installs'] ) {
431
						$active_installs_text = _x( 'Less Than 10', 'Active plugin installations' );
432
					} else {
433
						$active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
434
					}
435
					printf( __( '%s Active Installations' ), $active_installs_text );
436
					?>
437
				</div>
438
			</div>
439
		</li>
440
			<?php
441
442
		}
443
444
	}
445
446
}
447