Açıklama Yok

class-admin-bar-notice.php 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. <?php
  2. /**
  3. * A class that adds the scan notice to the admin bar.
  4. *
  5. * @package automattic/jetpack
  6. */
  7. namespace Automattic\Jetpack\Scan;
  8. use Automattic\Jetpack\Assets;
  9. use Automattic\Jetpack\Redirect;
  10. /**
  11. * Class Main
  12. *
  13. * Responsible for loading the admin bar notice if threats are found.
  14. *
  15. * @package Automattic\Jetpack\Scan
  16. */
  17. class Admin_Bar_Notice {
  18. const SCRIPT_NAME = 'jetpack-scan-show-notice';
  19. const SCRIPT_VERSION = '1';
  20. /**
  21. * The singleton instance of this class.
  22. *
  23. * @var Admin_Bar_Notice
  24. */
  25. protected static $instance;
  26. /**
  27. * Get the singleton instance of the class.
  28. *
  29. * @return Admin_Bar_Notice
  30. */
  31. public static function instance() {
  32. if ( ! isset( self::$instance ) ) {
  33. self::$instance = new Admin_Bar_Notice();
  34. self::$instance->init_hooks();
  35. }
  36. return self::$instance;
  37. }
  38. /**
  39. * Initalize the hooks as needed.
  40. */
  41. private function init_hooks() {
  42. if ( ! $this->should_try_to_display_notice() ) {
  43. return;
  44. }
  45. add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_toolbar_script' ) );
  46. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_toolbar_script' ) );
  47. add_action( 'admin_bar_menu', array( $this, 'add_threats_to_toolbar' ), 999 );
  48. // Inject the data-ampdevmode attribute into the inline <script> output via wp_localize_script(). To revisit after https://github.com/ampproject/amp-wp/issues/4598.
  49. add_filter(
  50. 'amp_dev_mode_element_xpaths',
  51. static function ( $expressions ) {
  52. $expressions[] = '//script[ contains( text(), "Jetpack_Scan" ) ]';
  53. return $expressions;
  54. }
  55. );
  56. }
  57. /**
  58. * Whether to even try to display the notice or now.
  59. *
  60. * @return bool
  61. */
  62. private function should_try_to_display_notice() {
  63. // Jetpack Scan is currently not supported on multisite.
  64. if ( is_multisite() ) {
  65. return false;
  66. }
  67. // Check if VaultPress is active, the assumtion there is that VaultPress is working.
  68. // It has its own notice in the admin bar.
  69. if ( class_exists( 'VaultPress' ) ) {
  70. return false;
  71. }
  72. // Only show the notice to admins.
  73. if ( ! current_user_can( 'manage_options' ) ) {
  74. return false;
  75. }
  76. return true;
  77. }
  78. /**
  79. * Add the inline styles and scripts if they are needed.
  80. */
  81. public function enqueue_toolbar_script() {
  82. $this->add_inline_styles();
  83. if ( ! is_null( $this->has_threats() ) ) {
  84. return;
  85. }
  86. // We don't know about threats in the cache lets load the JS that fetches the info and updates the admin bar.
  87. Assets::enqueue_async_script( self::SCRIPT_NAME, '_inc/build/scan/admin-bar-notice.min.js', 'modules/scan/admin-bar-notice.js', array( 'admin-bar' ), self::SCRIPT_VERSION, true );
  88. $script_data = array(
  89. 'nonce' => wp_create_nonce( 'wp_rest' ),
  90. 'scan_endpoint' => get_rest_url( null, 'jetpack/v4/scan' ),
  91. 'scan_dashboard_url' => Redirect::get_url( 'calypso-scanner' ),
  92. /* translators: %s is the alert icon */
  93. 'singular' => sprintf( esc_html__( '%s Threat found', 'jetpack' ), $this->get_icon() ),
  94. /* translators: %s is the alert icon */
  95. 'multiple' => sprintf( esc_html__( '%s Threats found', 'jetpack' ), $this->get_icon() ),
  96. );
  97. wp_localize_script( self::SCRIPT_NAME, 'Jetpack_Scan', $script_data );
  98. }
  99. /**
  100. * Adds the inline styles if they are needed.
  101. */
  102. public function add_inline_styles() {
  103. // We know there are no threats so lets not include any css.
  104. if ( false === $this->has_threats() ) {
  105. return;
  106. }
  107. // We might be showing the threats in the admin bar lets make sure that they look great!
  108. $hide_wording_on_mobile = '#wp-admin-bar-jetpack-scan-notice .is-hidden { display:none; } @media screen and (max-width: 959px ) { #wpadminbar #wp-admin-bar-jetpack-scan-notice { width:32px; } #wpadminbar #wp-admin-bar-jetpack-scan-notice a { color: transparent!important; } }';
  109. $style = '#wp-admin-bar-jetpack-scan-notice svg { float:left; margin-top: 4px; margin-right: 6px; width: 18px; height: 22px; }' . $hide_wording_on_mobile;
  110. if ( is_rtl() ) {
  111. $style = '#wp-admin-bar-jetpack-scan-notice svg { float:right; margin-top: 4px; margin-left: 6px; width: 18px; height: 22px; }' . $hide_wording_on_mobile;
  112. }
  113. wp_add_inline_style( 'admin-bar', $style );
  114. }
  115. /**
  116. * Add the link to the admin bar.
  117. *
  118. * @param WP_Admin_Bar $wp_admin_bar WP Admin Bar class object.
  119. */
  120. public function add_threats_to_toolbar( $wp_admin_bar ) {
  121. if ( ! $this->should_try_to_display_notice() ) {
  122. return;
  123. }
  124. $has_threats = $this->has_threats();
  125. if ( false === $has_threats ) {
  126. return;
  127. }
  128. $node = array(
  129. 'id' => 'jetpack-scan-notice',
  130. 'title' => '',
  131. 'parent' => 'top-secondary',
  132. 'meta' => array(
  133. 'title' => esc_attr__( 'View security scan details', 'jetpack' ),
  134. 'class' => 'error is-hidden',
  135. ),
  136. );
  137. if ( $has_threats ) {
  138. $node['href'] = esc_url( Redirect::get_url( 'calypso-scanner' ) );
  139. $node['meta']['onclick'] = 'window.open( this.href ); return false;';
  140. $node['meta']['class'] = 'error';
  141. $node['title'] = sprintf(
  142. esc_html(
  143. /* translators: %s is the alert icon */
  144. _n( '%s Threat found', '%s Threats found', $this->get_threat_count(), 'jetpack' )
  145. ),
  146. $this->get_icon()
  147. );
  148. }
  149. $wp_admin_bar->add_node( $node );
  150. }
  151. /**
  152. * Returns the shield icon.
  153. *
  154. * @return string
  155. */
  156. private function get_icon() {
  157. return '<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 0L0 4V10C0 15.55 3.84 20.74 9 22C14.16 20.74 18 15.55 18 10V4L9 0Z" fill="#D63638"/><path d="M7.99121 6.00894H10.0085V11.9968H7.99121V6.00894Z" fill="#FFF"/><path d="M7.99121 14.014H10.0085V15.9911H7.99121V14.014Z" fill="#FFF"/></svg>';
  158. }
  159. /**
  160. *
  161. * Return Whether boolean cached threats exist or null if the state is unknown.
  162. * * @return boolean or null
  163. */
  164. public function has_threats() {
  165. $scan_state = get_transient( 'jetpack_scan_state' );
  166. if ( empty( $scan_state ) ) {
  167. return null;
  168. }
  169. // Return true if there is at least one threat found.
  170. return (bool) isset( $scan_state->threats[0] );
  171. }
  172. /**
  173. * Returns the number of threats found or 0.
  174. *
  175. * @return int
  176. */
  177. public function get_threat_count() {
  178. if ( ! $this->has_threats() ) {
  179. return 0;
  180. }
  181. $scan_state = get_transient( 'jetpack_scan_state' );
  182. return is_array( $scan_state->threats ) ? count( $scan_state->threats ) : 0;
  183. }
  184. }