Нема описа

class-jetpack-about-page.php 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. <?php
  2. /**
  3. * Class for the Jetpack About Page within the wp-admin.
  4. *
  5. * @package automattic/jetpack
  6. */
  7. /**
  8. * Disable direct access and execution.
  9. */
  10. if ( ! defined( 'ABSPATH' ) ) {
  11. exit;
  12. }
  13. require_once 'class.jetpack-admin-page.php';
  14. /**
  15. * Builds the landing page and its menu.
  16. */
  17. class Jetpack_About_Page extends Jetpack_Admin_Page {
  18. /**
  19. * Show the settings page only when Jetpack is connected or in dev mode.
  20. *
  21. * @var bool If the page should be shown.
  22. */
  23. protected $dont_show_if_not_active = true;
  24. /**
  25. * Anonymous info about a12s. The method fetch_a8c_data() stores the response from wpcom here.
  26. *
  27. * @var array
  28. */
  29. private $a8c_data = null;
  30. /**
  31. * Add a submenu item to the Jetpack admin menu.
  32. *
  33. * @return string
  34. */
  35. public function get_page_hook() {
  36. // Add the main admin Jetpack menu.
  37. return add_submenu_page(
  38. null,
  39. esc_html__( 'About Jetpack', 'jetpack' ),
  40. '',
  41. 'jetpack_admin_page',
  42. 'jetpack_about',
  43. array( $this, 'render' )
  44. );
  45. }
  46. /**
  47. * Add page action
  48. *
  49. * @param string $hook Hook of current page, unused.
  50. */
  51. public function add_page_actions( $hook ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
  52. $this->a8c_data = $this->fetch_a8c_data();
  53. }
  54. /**
  55. * Enqueues scripts and styles for the admin page.
  56. */
  57. public function page_admin_scripts() {
  58. wp_enqueue_style( 'plugin-install' );
  59. wp_enqueue_script( 'plugin-install' );
  60. // required for plugin modal action button functionality.
  61. wp_enqueue_script( 'updates' );
  62. // required for modal popup JS and styling.
  63. wp_enqueue_style( 'thickbox' );
  64. wp_enqueue_script( 'thickbox' );
  65. }
  66. /**
  67. * Load styles for static page.
  68. */
  69. public function additional_styles() {
  70. Jetpack_Admin_Page::load_wrapper_styles();
  71. }
  72. /**
  73. * Render the page with a common top and bottom part, and page specific content
  74. */
  75. public function render() {
  76. Jetpack_Admin_Page::wrap_ui( array( $this, 'page_render' ), array( 'show-nav' => false ) );
  77. }
  78. /**
  79. * Render the page content
  80. */
  81. public function page_render() {
  82. ?>
  83. <div class="jp-lower">
  84. <div class="jetpack-about__link-back">
  85. <a href="<?php echo esc_url( admin_url( 'admin.php?page=jetpack' ) ); ?>">
  86. <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>
  87. <?php esc_html_e( 'Back to Jetpack Dashboard', 'jetpack' ); ?>
  88. </a>
  89. </div>
  90. <div class="jetpack-about__main">
  91. <div class="jetpack-about__logo">
  92. <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" viewBox="0 0 800 96" style="enable-background:new 0 0 800 96;" xml:space="preserve">
  93. <g>
  94. <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
  95. 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
  96. 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
  97. s22.122-9.803,22.122-20.614V47.245z"/>
  98. <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
  99. L33.401,54.369h24.72L45.552,30.906z"/>
  100. <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
  101. c13.242,0,18.687-6.116,18.687-16.424V20.012h10.475v32.598C158.342,66.436,149.46,78,128.427,78z"/>
  102. <path d="M216.667,28.727v47.094h-10.475V28.727h-24.386v-8.715h59.245v8.715H216.667z"/>
  103. <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
  104. l22.038,38.296L390.713,63l2.599-4.692l21.786-38.296h14.331v55.81H418.955z"/>
  105. <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
  106. l-12.151,23.464h24.72L484.569,30.906z"/>
  107. <path d="M562.081,28.727v47.094h-10.474V28.727h-24.386v-8.715h59.245v8.715H562.081z"/>
  108. <path d="M638.924,28.727v47.094H628.45V28.727h-24.386v-8.715h59.245v8.715H638.924z"/>
  109. <path d="M689.118,75.821v-50.53c4.19,0,5.866-2.263,5.866-5.28h4.442v55.81H689.118z"/>
  110. <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
  111. 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
  112. 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
  113. L781.464,35.765z"/>
  114. <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
  115. 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"/>
  116. </g>
  117. </svg>
  118. </div>
  119. <div class="jetpack-about__content">
  120. <div class="jetpack-about__images">
  121. <ul class="jetpack-about__gravatars">
  122. <?php $this->display_gravatars(); ?>
  123. </ul>
  124. <p class="meet-the-team">
  125. <a href="https://automattic.com/about/" target="_blank" rel="noopener noreferrer" class="jptracks" data-jptracks-name="jetpack_about_meet_the_team"><?php esc_html_e( 'Meet the Automattic team', 'jetpack' ); ?></a>
  126. </p>
  127. </div>
  128. <div class="jetpack-about__text">
  129. <p>
  130. <?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' ); ?>
  131. <a href="https://automattic.com/" target="_blank" rel="noopener noreferrer" class="jptracks" data-jptracks-name="jetpack_about_learn_more">
  132. <?php esc_html_e( 'Learn more about us.', 'jetpack' ); ?>
  133. </a>
  134. </p>
  135. <p>
  136. <?php
  137. echo esc_html(
  138. sprintf(
  139. /* translators: first placeholder is the number of Automattic employees. The second is the number of countries of origin*/
  140. __( 'We’re a distributed company with over %1$s Automatticians in more than %2$s countries speaking at least %3$s 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' ),
  141. $this->a8c_data['a12s'],
  142. $this->a8c_data['countries'],
  143. $this->a8c_data['languages']
  144. )
  145. );
  146. ?>
  147. </p>
  148. <p>
  149. <?php esc_html_e( 'We believe in Open Source and the vast majority of our work is available under the GPL.', 'jetpack' ); ?>
  150. </p>
  151. <p>
  152. <?php
  153. // 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().
  154. echo wp_kses(
  155. __( 'We strive to live by the <a href="https://automattic.com/creed/" target="_blank" class="jptracks" data-jptracks-name="jetpack_about_creed" rel="noopener noreferrer">Automattic Creed</a>.', 'jetpack' ),
  156. array(
  157. 'a' => array(
  158. 'href' => array(),
  159. 'class' => array(),
  160. 'target' => array(),
  161. 'rel' => array(),
  162. 'data-jptracks-name' => array(),
  163. ),
  164. )
  165. );
  166. ?>
  167. </p>
  168. <p>
  169. <a href="https://automattic.com/work-with-us" target="_blank" rel="noopener noreferrer" class="jptracks" data-jptracks-name="jetpack_about_work_with_us">
  170. <?php esc_html_e( 'Come work with us', 'jetpack' ); ?>
  171. </a>
  172. </p>
  173. </div>
  174. </div>
  175. </div>
  176. <div class="jetpack-about__colophon">
  177. <h3><?php esc_html_e( 'Popular WordPress services by Automattic', 'jetpack' ); ?></h3>
  178. <ul class="jetpack-about__services">
  179. <?php $this->display_plugins(); ?>
  180. </ul>
  181. <p class="jetpack-about__services-more">
  182. <?php
  183. echo wp_kses(
  184. __( 'For even more of our WordPress plugins, please <a href="https://profiles.wordpress.org/automattic/#content-plugins" target="_blank" rel="noopener noreferrer" class="jptracks" data-jptracks-name="jetpack_about_wporg_profile">take a look at our WordPress.org profile</a>.', 'jetpack' ),
  185. array(
  186. 'a' => array(
  187. 'href' => array(),
  188. 'target' => array(),
  189. 'rel' => array(),
  190. 'class' => array(),
  191. 'data-jptracks-name' => array(),
  192. ),
  193. )
  194. );
  195. ?>
  196. </p>
  197. </div>
  198. </div>
  199. <?php
  200. }
  201. /**
  202. * Add information cards for a8c plugins.
  203. */
  204. public function display_plugins() {
  205. $plugins_allowedtags = array(
  206. 'a' => array(
  207. 'href' => array(),
  208. 'title' => array(),
  209. 'target' => array(),
  210. ),
  211. 'abbr' => array( 'title' => array() ),
  212. 'acronym' => array( 'title' => array() ),
  213. 'code' => array(),
  214. 'pre' => array(),
  215. 'em' => array(),
  216. 'strong' => array(),
  217. 'ul' => array(),
  218. 'ol' => array(),
  219. 'li' => array(),
  220. 'p' => array(),
  221. 'br' => array(),
  222. );
  223. // slugs for plugins we want to display.
  224. $a8c_plugins = $this->a8c_data['featured_plugins'];
  225. // need this to access the plugins_api() function.
  226. include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
  227. $plugins = array();
  228. foreach ( $a8c_plugins as $slug ) {
  229. $args = array(
  230. 'slug' => $slug,
  231. 'fields' => array(
  232. 'added' => false,
  233. 'author' => false,
  234. 'author_profile' => false,
  235. 'banners' => false,
  236. 'contributors' => false,
  237. 'donate_link' => false,
  238. 'homepage' => false,
  239. 'reviews' => false,
  240. 'screenshots' => false,
  241. 'support_threads' => false,
  242. 'support_threads_resolved' => false,
  243. 'sections' => false,
  244. 'tags' => false,
  245. 'versions' => false,
  246. 'compatibility' => true,
  247. 'downloaded' => true,
  248. 'downloadlink' => true,
  249. 'icons' => true,
  250. 'last_updated' => true,
  251. 'num_ratings' => true,
  252. 'rating' => true,
  253. 'requires' => true,
  254. 'requires_php' => true,
  255. 'short_description' => true,
  256. 'tested' => true,
  257. ),
  258. );
  259. // should probably add some error checking here too.
  260. $api = plugins_api( 'plugin_information', $args );
  261. $plugins[] = $api;
  262. }
  263. foreach ( $plugins as $plugin ) {
  264. if ( is_object( $plugin ) ) {
  265. $plugin = (array) $plugin;
  266. }
  267. $title = wp_kses( $plugin['name'], $plugins_allowedtags );
  268. $version = wp_kses( $plugin['version'], $plugins_allowedtags );
  269. $name = wp_strip_all_tags( $title . ' ' . $version );
  270. // Remove any HTML from the description.
  271. $description = wp_strip_all_tags( $plugin['short_description'] );
  272. $wp_version = get_bloginfo( 'version' );
  273. $compatible_php = ( empty( $plugin['requires_php'] ) || version_compare( phpversion(), $plugin['requires_php'], '>=' ) );
  274. $compatible_wp = ( empty( $plugin['requires'] ) || version_compare( $wp_version, $plugin['requires'], '>=' ) );
  275. $action_links = array();
  276. // install button.
  277. if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) {
  278. $status = install_plugin_install_status( $plugin );
  279. switch ( $status['status'] ) {
  280. case 'install':
  281. if ( $status['url'] ) {
  282. if ( $compatible_php && $compatible_wp ) {
  283. $action_links[] = sprintf(
  284. '<a class="install-now button jptracks" data-slug="%1$s" href="%2$s" aria-label="%3$s" data-name="%4$s" data-jptracks-name="jetpack_about_install_button" data-jptracks-prop="%4$s">%5$s</a>',
  285. esc_attr( $plugin['slug'] ),
  286. esc_url( $status['url'] ),
  287. /* translators: %s: plugin name and version */
  288. esc_attr( sprintf( __( 'Install %s now', 'jetpack' ), $name ) ),
  289. esc_attr( $name ),
  290. esc_html__( 'Install Now', 'jetpack' )
  291. );
  292. } else {
  293. $action_links[] = sprintf(
  294. '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
  295. _x( 'Cannot Install', 'plugin', 'jetpack' )
  296. );
  297. }
  298. }
  299. break;
  300. case 'update_available':
  301. if ( $status['url'] ) {
  302. $action_links[] = sprintf(
  303. '<a class="update-now button aria-button-if-js jptracks" data-plugin="%1$s" data-slug="%2$s" href="%3$s" aria-label="%4$s" data-name="%5$s" data-jptracks-name="jetpack_about_update_button" data-jptracks-prop="%5$s">%6$s</a>',
  304. esc_attr( $status['file'] ),
  305. esc_attr( $plugin['slug'] ),
  306. esc_url( $status['url'] ),
  307. /* translators: %s: plugin name and version */
  308. esc_attr( sprintf( __( 'Update %s now', 'jetpack' ), $name ) ),
  309. esc_attr( $name ),
  310. __( 'Update Now', 'jetpack' )
  311. );
  312. }
  313. break;
  314. case 'latest_installed':
  315. case 'newer_installed':
  316. if ( is_plugin_active( $status['file'] ) ) {
  317. $action_links[] = sprintf(
  318. '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
  319. _x( 'Active', 'plugin', 'jetpack' )
  320. );
  321. } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) {
  322. $button_text = __( 'Activate', 'jetpack' );
  323. /* translators: %s: plugin name */
  324. $button_label = _x( 'Activate %s', 'plugin', 'jetpack' );
  325. $activate_url = add_query_arg(
  326. array(
  327. '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
  328. 'action' => 'activate',
  329. 'plugin' => $status['file'],
  330. ),
  331. network_admin_url( 'plugins.php' )
  332. );
  333. if ( is_network_admin() ) {
  334. $button_text = __( 'Network Activate', 'jetpack' );
  335. /* translators: %s: plugin name */
  336. $button_label = _x( 'Network Activate %s', 'plugin', 'jetpack' );
  337. $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url );
  338. }
  339. $action_links[] = sprintf(
  340. '<a href="%1$s" class="button activate-now" aria-label="%2$s" data-jptracks-name="jetpack_about_activate_button" data-jptracks-prop="%3$s">%4$s</a>',
  341. esc_url( $activate_url ),
  342. esc_attr( sprintf( $button_label, $plugin['name'] ) ),
  343. esc_attr( $plugin['name'] ),
  344. $button_text
  345. );
  346. } else {
  347. $action_links[] = sprintf(
  348. '<button type="button" class="button button-disabled" disabled="disabled">%s</button>',
  349. _x( 'Installed', 'plugin', 'jetpack' )
  350. );
  351. }
  352. break;
  353. }
  354. }
  355. $plugin_install = "plugin-install.php?tab=plugin-information&amp;plugin={$plugin['slug']}&amp;TB_iframe=true&amp;width=600&amp;height=550";
  356. $details_link = is_multisite()
  357. ? network_admin_url( $plugin_install )
  358. : admin_url( $plugin_install );
  359. if ( ! empty( $plugin['icons']['svg'] ) ) {
  360. $plugin_icon_url = $plugin['icons']['svg'];
  361. } elseif ( ! empty( $plugin['icons']['2x'] ) ) {
  362. $plugin_icon_url = $plugin['icons']['2x'];
  363. } elseif ( ! empty( $plugin['icons']['1x'] ) ) {
  364. $plugin_icon_url = $plugin['icons']['1x'];
  365. } else {
  366. $plugin_icon_url = $plugin['icons']['default'];
  367. }
  368. ?>
  369. <li class="jetpack-about__plugin plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>">
  370. <?php
  371. if ( ! $compatible_php || ! $compatible_wp ) {
  372. echo '<div class="notice inline notice-error notice-alt"><p>';
  373. if ( ! $compatible_php && ! $compatible_wp ) {
  374. esc_html_e( 'This plugin doesn&#8217;t work with your versions of WordPress and PHP.', 'jetpack' );
  375. if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) {
  376. printf(
  377. /* translators: 1: "Update WordPress" screen URL, 2: "Update PHP" page URL */
  378. ' ' . wp_kses( __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ),
  379. esc_url( self_admin_url( 'update-core.php' ) ),
  380. esc_url( wp_get_update_php_url() )
  381. );
  382. wp_update_php_annotation();
  383. } elseif ( current_user_can( 'update_core' ) ) {
  384. printf(
  385. /* translators: %s: "Update WordPress" screen URL */
  386. ' ' . wp_kses( __( '<a href="%s">Please update WordPress</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ),
  387. esc_url( self_admin_url( 'update-core.php' ) )
  388. );
  389. } elseif ( current_user_can( 'update_php' ) ) {
  390. printf(
  391. /* translators: %s: "Update PHP" page URL */
  392. ' ' . wp_kses( __( '<a href="%s">Learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ),
  393. esc_url( wp_get_update_php_url() )
  394. );
  395. wp_update_php_annotation();
  396. }
  397. } elseif ( ! $compatible_wp ) {
  398. esc_html_e( 'This plugin doesn&#8217;t work with your version of WordPress.', 'jetpack' );
  399. if ( current_user_can( 'update_core' ) ) {
  400. printf(
  401. /* translators: %s: "Update WordPress" screen URL */
  402. ' ' . wp_kses( __( '<a href="%s">Please update WordPress</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ),
  403. esc_url( self_admin_url( 'update-core.php' ) )
  404. );
  405. }
  406. } elseif ( ! $compatible_php ) {
  407. esc_html_e( 'This plugin doesn&#8217;t work with your version of PHP.', 'jetpack' );
  408. if ( current_user_can( 'update_php' ) ) {
  409. printf(
  410. /* translators: %s: "Update PHP" page URL */
  411. ' ' . wp_kses( __( '<a href="%s">Learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ),
  412. esc_url( wp_get_update_php_url() )
  413. );
  414. wp_update_php_annotation();
  415. }
  416. }
  417. echo '</p></div>';
  418. }
  419. ?>
  420. <div class="plugin-card-top">
  421. <div class="name column-name">
  422. <h3>
  423. <a href="<?php echo esc_url( $details_link ); ?>" class="jptracks thickbox open-plugin-details-modal" data-jptracks-name="jetpack_about_plugin_modal" data-jptracks-prop="<?php echo esc_attr( $plugin['slug'] ); ?>">
  424. <?php echo esc_html( $title ); ?>
  425. <img src="<?php echo esc_url( $plugin_icon_url ); ?>" class="plugin-icon" alt="<?php esc_attr_e( 'Plugin icon', 'jetpack' ); ?>" aria-hidden="true">
  426. </a>
  427. </h3>
  428. </div>
  429. <div class="desc column-description">
  430. <p><?php echo esc_html( $description ); ?></p>
  431. </div>
  432. <div class="details-link">
  433. <a class="jptracks thickbox open-plugin-details-modal" href="<?php echo esc_url( $details_link ); ?>" data-jptracks-name="jetpack_about_plugin_details_modal" data-jptracks-prop="<?php echo esc_attr( $plugin['slug'] ); ?>"><?php esc_html_e( 'More Details', 'jetpack' ); ?></a>
  434. </div>
  435. </div>
  436. <div class="plugin-card-bottom">
  437. <div class="meta">
  438. <?php
  439. wp_star_rating(
  440. array(
  441. 'rating' => $plugin['rating'],
  442. 'type' => 'percent',
  443. 'number' => $plugin['num_ratings'],
  444. )
  445. );
  446. ?>
  447. <span class="num-ratings" aria-hidden="true">(<?php echo esc_html( number_format_i18n( $plugin['num_ratings'] ) ); ?> <?php esc_html_e( 'ratings', 'jetpack' ); ?>)</span>
  448. <div class="downloaded">
  449. <?php
  450. if ( $plugin['active_installs'] >= 1000000 ) {
  451. $active_installs_millions = floor( $plugin['active_installs'] / 1000000 );
  452. $active_installs_text = sprintf(
  453. /* translators: number of millions of installs. */
  454. _nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations', 'jetpack' ),
  455. number_format_i18n( $active_installs_millions )
  456. );
  457. } elseif ( 0 === $plugin['active_installs'] ) {
  458. $active_installs_text = _x( 'Less Than 10', 'Active plugin installations', 'jetpack' );
  459. } else {
  460. $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+';
  461. }
  462. /* translators: number of active installs */
  463. printf( esc_html__( '%s Active Installations', 'jetpack' ), esc_html( $active_installs_text ) );
  464. ?>
  465. </div>
  466. </div>
  467. <div class="action-links">
  468. <?php
  469. if ( $action_links ) {
  470. // The var simply collects strings that have already been sanitized.
  471. // phpcs:ignore WordPress.Security.EscapeOutput
  472. echo '<ul class="action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>';
  473. }
  474. ?>
  475. </div>
  476. </div>
  477. </li>
  478. <?php
  479. }
  480. }
  481. /**
  482. * Fetch anonymous data about A12s from wpcom: total count, number of countries, languages spoken.
  483. *
  484. * @since 7.4
  485. *
  486. * @return array $data
  487. */
  488. private function fetch_a8c_data() {
  489. $data = get_transient( 'jetpack_a8c_data' );
  490. if ( false === $data ) {
  491. $data = json_decode(
  492. wp_remote_retrieve_body(
  493. wp_remote_get( 'https://public-api.wordpress.com/wpcom/v2/jetpack-about' )
  494. ),
  495. true
  496. );
  497. if ( ! empty( $data ) && is_array( $data ) ) {
  498. set_transient( 'jetpack_a8c_data', $data, DAY_IN_SECONDS );
  499. } else {
  500. // Fallback if everything fails.
  501. $data = array(
  502. 'a12s' => 888,
  503. 'countries' => 69,
  504. 'languages' => 83,
  505. 'featured_plugins' => array(
  506. 'woocommerce',
  507. 'wp-super-cache',
  508. 'wp-job-manager',
  509. 'co-authors-plus',
  510. ),
  511. );
  512. }
  513. }
  514. return $data;
  515. }
  516. /**
  517. * Compile and display a list of avatars for A12s that gave their permission.
  518. *
  519. * @since 7.3
  520. */
  521. public function display_gravatars() {
  522. $hashes = array(
  523. 'https://1.gravatar.com/avatar/d2ab03dbab0c97740be75f290a2e3190',
  524. 'https://2.gravatar.com/avatar/b0b357b291ac72bc7da81b4d74430fe6',
  525. 'https://2.gravatar.com/avatar/9e149207a0e0818abed0edbb1fb2d0bf',
  526. 'https://2.gravatar.com/avatar/9f376366854d750124dffe057dda99c9',
  527. 'https://1.gravatar.com/avatar/1c75d26ad0d38624f02b15accc1f20cd',
  528. 'https://1.gravatar.com/avatar/c510e69d83c7d10be4df64feeff4e46a',
  529. 'https://0.gravatar.com/avatar/88ec0dcadea38adf5f30a17e54e9b248',
  530. 'https://1.gravatar.com/avatar/1ec3571e0201a990ceca5e365e780efa',
  531. 'https://0.gravatar.com/avatar/0619d4de8aef78c81b2194ff1d164d85',
  532. 'https://0.gravatar.com/avatar/7fdcad31a04def0ab9583af475c9036c',
  533. 'https://0.gravatar.com/avatar/b3618d70c63bbc5cc7caee0beded5ff0',
  534. 'https://1.gravatar.com/avatar/4d346581a3340e32cf93703c9ce46bd4',
  535. 'https://2.gravatar.com/avatar/9c2f6b95a00dfccfadc6a912a2b859ba',
  536. 'https://1.gravatar.com/avatar/1a33e7a69df4f675fcd799edca088ac2',
  537. 'https://2.gravatar.com/avatar/d5dc443845c134f365519568d5d80e62',
  538. 'https://0.gravatar.com/avatar/c0ccdd53794779bcc07fcae7b79c4d80',
  539. );
  540. $output = '';
  541. foreach ( $hashes as $hash ) {
  542. $output .= '<li><img src="' . esc_url( $hash ) . '?s=150"></li>' . "\n";
  543. }
  544. echo wp_kses(
  545. $output,
  546. array(
  547. 'li' => true,
  548. 'img' => array(
  549. 'src' => true,
  550. ),
  551. )
  552. );
  553. }
  554. }