Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
Admin_Sidebar_Link
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 13
1260
0.00% covered (danger)
0.00%
0 / 1
 instance
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 init_hooks
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 maybe_add_admin_link
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
30
 refresh_state_cache
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 should_show_link
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
30
 should_show_scan
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 should_show_scan_history_only
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 should_show_backup
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 has_scan
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 has_protect_plugin
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 has_backup
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 has_backup_plugin
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 maybe_refresh_transient_cache
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * A class that adds a scan and backup link to the admin sidebar.
4 *
5 * @package automattic/jetpack
6 */
7
8namespace Automattic\Jetpack\Scan;
9
10use Automattic\Jetpack\Admin_UI\Admin_Menu;
11use Automattic\Jetpack\My_Jetpack\Products\Backup;
12use Automattic\Jetpack\Redirect;
13use Automattic\Jetpack\Status\Host;
14use Jetpack_Core_Json_Api_Endpoints;
15
16/**
17 * Class Main
18 *
19 * Responsible for showing the link if available.
20 *
21 * @package Automattic\Jetpack\Scan
22 */
23class Admin_Sidebar_Link {
24
25    const SCHEDULE_ACTION_HOOK = 'jetpack_scan_refresh_states_event';
26
27    /**
28     * The singleton instance of this class.
29     *
30     * @var Admin_Sidebar_Link
31     */
32    protected static $instance;
33
34    /**
35     * Used to check if we need to schedule the refresh or we need to do it.
36     *
37     * @var boolean | null
38     */
39    private $schedule_refresh_checked;
40
41    /**
42     * Get the singleton instance of the class.
43     *
44     * @return Admin_Sidebar_Link
45     */
46    public static function instance() {
47        if ( ! isset( self::$instance ) ) {
48            self::$instance = new Admin_Sidebar_Link();
49            self::$instance->init_hooks();
50        }
51
52        return self::$instance;
53    }
54
55    /**
56     * Adds action hooks.
57     */
58    public function init_hooks() {
59        add_action( 'jetpack_admin_menu', array( $this, 'maybe_add_admin_link' ), 99 );
60        add_action( self::SCHEDULE_ACTION_HOOK, array( $this, 'refresh_state_cache' ) );
61    }
62
63    /**
64     * Adds a link to the Scan and Backup page.
65     */
66    public function maybe_add_admin_link() {
67        if ( ! $this->should_show_link() ) {
68            return;
69        }
70
71        if ( $this->should_show_scan() ) {
72            Admin_Menu::add_menu(
73                /** "Scan" is a product name, do not translate. */
74                'Scan',
75                'Scan <span aria-hidden="true">↗</span>',
76                'manage_options',
77                esc_url( Redirect::get_url( 'cloud-scan-history-wp-menu' ) ),
78                null,
79                100
80            );
81        }
82
83        // Add scan item which shows history page only. This is mutally exclusive from the scan item above and is only shown for Atomic sitse.
84        if ( $this->should_show_scan_history_only() ) {
85            Admin_Menu::add_menu(
86                /** "Scan" is a product name, do not translate. */
87                'Scan',
88                'Scan <span aria-hidden="true">↗</span>',
89                'manage_options',
90                esc_url( Redirect::get_url( 'cloud-scan-history-wp-menu' ) ),
91                null,
92                100
93            );
94        }
95
96        if ( $this->should_show_backup() ) {
97            Admin_Menu::add_menu(
98                /** "VaultPress Backup" is a product name, do not translate. */
99                'VaultPress Backup',
100                'VaultPress Backup <span aria-hidden="true">↗</span>',
101                'manage_options',
102                esc_url( Redirect::get_url( 'calypso-backups' ) ),
103                null,
104                100
105            );
106        }
107    }
108
109    /**
110     * Refreshes the state cache via API call. Called via cron.
111     */
112    public function refresh_state_cache() {
113        Jetpack_Core_Json_Api_Endpoints::get_scan_state();
114        Jetpack_Core_Json_Api_Endpoints::get_rewind_data();
115    }
116
117    /**
118     * Returns true if the link should appear.
119     *
120     * @return boolean
121     */
122    private function should_show_link() {
123        // Jetpack Scan/Backup is currently not supported on multisite.
124        if ( is_multisite() ) {
125            return false;
126        }
127
128        // Check if VaultPress is active, the assumption there is that VaultPress is working.
129        // It has its link the adminbar.
130        if ( class_exists( 'VaultPress' ) ) {
131            return false;
132        }
133
134        return $this->should_show_scan() || $this->should_show_backup() || $this->should_show_scan_history_only();
135    }
136
137    /**
138     * Check if we should display the Scan menu item.
139     *
140     * It will only be displayed if site has Scan enabled, is not an Atomic site, and the stand-alone Protect plugin is not active, because it will have a menu item of its own.
141     *
142     * @return boolean
143     */
144    private function should_show_scan() {
145        return $this->has_scan() && ! $this->has_protect_plugin() && ! ( new Host() )->is_woa_site();
146    }
147
148    /**
149     * Check if we should display the Scan menu item history.
150     *
151     * It will only be displayed if site has Scan enabled, is an Atomic site.
152     *
153     * @return boolean
154     */
155    private function should_show_scan_history_only() {
156        return $this->has_scan() && ( new Host() )->is_woa_site() && get_option( 'wpcom_admin_interface' ) === 'wp-admin';
157    }
158
159    /**
160     * Check if we should display the Backup menu item.
161     *
162     * It will only be displayed if site has Backup enabled and the stand-alone Backup plugin is not active, because it will have a menu item of its own.
163     *
164     * @return boolean
165     */
166    private function should_show_backup() {
167        return $this->has_backup() && ! $this->has_backup_plugin();
168    }
169
170    /**
171     * Detects if Scan is enabled.
172     *
173     * @return boolean
174     */
175    private function has_scan() {
176        $this->maybe_refresh_transient_cache();
177        $scan_state = get_transient( 'jetpack_scan_state' );
178        if ( ! $scan_state ) {
179            return false;
180        }
181
182        return isset( $scan_state->state ) && 'unavailable' !== $scan_state->state;
183    }
184
185    /**
186     * Detects if Protect plugin is active.
187     *
188     * @return boolean
189     */
190    private function has_protect_plugin() {
191        return class_exists( 'Jetpack_Protect' );
192    }
193
194    /**
195     * Detects if Backup is enabled.
196     *
197     * @return boolean
198     */
199    private function has_backup() {
200        $this->maybe_refresh_transient_cache();
201        $rewind_state = get_transient( 'jetpack_rewind_state' );
202        if ( ! $rewind_state ) {
203            return false;
204        }
205
206        return isset( $rewind_state->state ) && 'unavailable' !== $rewind_state->state;
207    }
208
209    /**
210     * Detects if Backup plugin is active.
211     *
212     * @return boolean
213     */
214    private function has_backup_plugin() {
215        return Backup::is_standalone_plugin_active();
216    }
217
218    /**
219     * Triggers a cron job to refresh the Scan and Rewind state cache.
220     */
221    private function maybe_refresh_transient_cache() {
222        if ( $this->schedule_refresh_checked ) {
223            return;
224        }
225
226        // Do we have a jetpack_scan and jetpack_rewind state set?
227        if ( get_transient( 'jetpack_scan_state' ) && get_transient( 'jetpack_rewind_state' ) ) {
228            return;
229        }
230
231        if ( false === wp_next_scheduled( self::SCHEDULE_ACTION_HOOK ) ) {
232            wp_schedule_single_event( time(), self::SCHEDULE_ACTION_HOOK );
233        }
234
235        $this->schedule_refresh_checked = true;
236    }
237}