PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-backup/src/class-jetpack-backup.php +147 -36 16.2 → 16.3-beta View file →
@@ -22,8 +22,10 @@
22 22 use Automattic\Jetpack\Connection\Client;
23 23 use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
24 24 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
25 25 use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
26 +use Automattic\Jetpack\Constants;
27 +use Automattic\Jetpack\JITMS\JITM;
26 28 use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
27 29 use Automattic\Jetpack\Status;
28 30 use Automattic\Jetpack\Terms_Of_Service;
29 31 use Automattic\Jetpack\Tracking;
@@ -130,8 +132,15 @@
130 132 */
131 133 const MODERNIZATION_FILTER = 'rsm_jetpack_ui_modernization_backup';
132 134
133 135 /**
136 + * Blog sticker that takes a site out of the internal preview.
137 + *
138 + * Atomic sees it only if it is on WordPress.com's `atomic_site_stickers()` allowlist.
139 + */
140 + const LEGACY_DASHBOARD_STICKER = 'use-backup-legacy-dashboard';
141 +
142 + /**
134 143 * Rewind state read from WordPress.com, memoized for the request.
135 144 *
136 145 * A class property and not a function static so tests can clear it.
137 146 *
@@ -139,17 +148,36 @@
139 148 */
140 149 private static $rewind_state = null;
141 150
142 151 /**
152 + * The screen ID alias_screen_id_for_wp_build() replaced, until it is restored.
153 + *
154 + * @var string|null
155 + */
156 + private static $wp_build_original_screen_id = null;
157 +
158 + /**
159 + * Initialization options.
160 + *
161 + * @var array
162 + */
163 + const DEFAULT_INIT_OPTIONS = array(
164 + // A host that already ensured a connection under its own slug must not have it
165 + // re-ensured here as `jetpack-backup`, which would rename the site's connection.
166 + 'manage_connection' => true,
167 + );
168 +
169 + /**
143 170 * Constructor.
171 + *
172 + * @param array $options Overrides for self::DEFAULT_INIT_OPTIONS.
144 173 */
145 - public static function initialize() {
174 + public static function initialize( array $options = array() ) {
146 175 if ( did_action( 'jetpack_backup_initialized' ) ) {
147 176 return;
148 177 }
149 178
150 - // Set up the REST authentication hooks.
151 - Connection_Rest_Authentication::init();
179 + $options = array_merge( self::DEFAULT_INIT_OPTIONS, $options );
152 180
153 181 add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );
154 182 add_action( 'rest_api_init', array( \Automattic\Jetpack\Backup\V0005\REST\Rest_Controller::class, 'register_routes' ) );
155 183
@@ -155,8 +183,32 @@
155 183
156 184 add_action( 'admin_menu', array( __CLASS__, 'maybe_load_wp_build' ), 1 );
157 185 add_action( 'admin_menu', array( __CLASS__, 'add_wp_admin_submenu' ), 1 ); // Akismet uses 4, so we need to use 1 to ensure both menus are added when only they exist.
158 186
187 + if ( $options['manage_connection'] ) {
188 + self::init_standalone_connection();
189 + }
190 +
191 + // Jetpack Backup abilities are registered from `actions.php` at package
192 + // autoload time so the surface is available in every consumer that
193 + // loads this package (both the standalone Backup plugin and the
194 + // Jetpack plugin), not only when `Jetpack_Backup::initialize()` runs.
195 +
196 + /**
197 + * Runs right after the Jetpack Backup package is initialized.
198 + *
199 + * @since 1.3.0
200 + */
201 + do_action( 'jetpack_backup_initialized' );
202 + }
203 +
204 + /**
205 + * Set up the connection, sync and identity-crisis packages under the standalone plugin's slug.
206 + */
207 + private static function init_standalone_connection() {
208 + // Set up the REST authentication hooks.
209 + Connection_Rest_Authentication::init();
210 +
159 211 // Init Jetpack packages.
160 212 add_action(
161 213 'plugins_loaded',
162 214 function () {
@@ -181,20 +233,8 @@
181 233
182 234 add_action( 'plugins_loaded', array( __CLASS__, 'maybe_upgrade_db' ), 20 );
183 235
184 236 add_filter( 'jetpack_connection_user_has_license', array( __CLASS__, 'jetpack_check_user_licenses' ), 10, 3 );
185 -
186 - // Jetpack Backup abilities are registered from `actions.php` at package
187 - // autoload time so the surface is available in every consumer that
188 - // loads this package (both the standalone Backup plugin and the
189 - // Jetpack plugin), not only when `Jetpack_Backup::initialize()` runs.
190 -
191 - /**
192 - * Runs right after the Jetpack Backup package is initialized.
193 - *
194 - * @since 1.3.0
195 - */
196 - do_action( 'jetpack_backup_initialized' );
197 237 }
198 238
199 239 /**
200 240 * The page to be added to submenu
@@ -204,12 +244,12 @@
204 244 $callback = $wp_build_active
205 245 ? 'jetpack_backup_jetpack_backup_dashboard_wp_admin_render_page'
206 246 : array( __CLASS__, 'plugin_settings_page' );
207 247
208 - // The relabel rides the modernized dashboard rather than the filter alone,
248 + // The page title's relabel rides the modernized dashboard rather than the filter alone,
209 249 // so a fallback to the legacy page also falls back to the legacy title.
210 250 $page_title = $wp_build_active ? 'Jetpack VaultPress Backup' : 'Jetpack Backup';
211 - $menu_title = $wp_build_active ? 'VaultPress Backup' : 'Backup'; // Product name, do not translate.
251 + $menu_title = 'Backup'; // Product name, do not translate.
212 252
213 253 $page_suffix = Admin_Menu::add_menu(
214 254 $page_title,
215 255 $menu_title,
@@ -214,9 +254,14 @@
214 254 $page_title,
215 255 $menu_title,
216 256 'manage_options',
217 257 self::JETPACK_BACKUP_SLUG,
218 - $callback
258 + $callback,
259 + null,
260 + array(
261 + 'product' => 'backup',
262 + 'key' => 'jetpack-backup',
263 + )
219 264 );
220 265
221 266 if ( $page_suffix ) {
222 267 add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
@@ -229,15 +274,16 @@
229 274 public static function admin_init() {
230 275 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_scripts' ) );
231 276
232 277 if ( self::is_wp_build_dashboard_active() ) {
233 - // The modernized Backup overview is a focused, full-screen product
234 - // surface. Suppress JITMs and other core/plugin admin notices so they
235 - // don't reflow on top of the dual-pane layout. Mirrors how Jetpack
236 - // Forms handles its dashboard page
237 - // (`plugins/forms/src/dashboard/class-dashboard.php`).
238 - remove_all_actions( 'admin_notices' );
239 - remove_all_actions( 'all_admin_notices' );
278 + // Notices reflow the dual-pane layout, so clear them but keep our own.
279 + // An older jetpack-jitm may predate the helper; the fallback costs the JITM.
280 + if ( method_exists( JITM::class, 'suppress_foreign_admin_notices' ) ) {
281 + JITM::suppress_foreign_admin_notices();
282 + } else {
283 + remove_all_actions( 'admin_notices' );
284 + remove_all_actions( 'all_admin_notices' );
285 + }
240 286 }
241 287 }
242 288
243 289 /**
@@ -1117,9 +1163,9 @@
1117 1163 if ( ! self::is_modernized() || ! self::is_backup_admin_request() ) {
1118 1164 return;
1119 1165 }
1120 1166
1121 - self::load_wp_build();
1167 + self::load_wp_build_with_screen_alias();
1122 1168
1123 1169 // wp-build registers standalone modules (e.g. the init module) on
1124 1170 // wp_default_scripts, which has already fired by admin_menu. Register them
1125 1171 // directly so the init module makes it into the import map.
@@ -1126,9 +1172,8 @@
1126 1172 if ( function_exists( 'jetpack_backup_register_script_modules' ) ) {
1127 1173 jetpack_backup_register_script_modules(); // @phan-suppress-current-line PhanUndeclaredFunction -- Checked with function_exists(); defined in the generated build/modules.php, which Phan excludes.
1128 1174 }
1129 1175
1130 - add_action( 'current_screen', array( __CLASS__, 'alias_screen_id_for_wp_build' ) );
1131 1176 add_action( 'admin_print_scripts', array( __CLASS__, 'render_connection_initial_state' ), 1 );
1132 1177 }
1133 1178
1134 1179 /**
@@ -1153,10 +1198,10 @@
1153 1198
1154 1199 /**
1155 1200 * Load the wp-build entry file and register its polyfills.
1156 1201 *
1157 - * Only called on `?page=jetpack-backup` admin requests when the
1158 - * modernization filter is enabled. Keeps wp-build off every other request.
1202 + * Only called on `?page=jetpack-backup` admin requests when `is_modernized()`
1203 + * is true. Keeps wp-build off every other request.
1159 1204 *
1160 1205 * @return void
1161 1206 */
1162 1207 private static function load_wp_build() {
@@ -1177,8 +1222,32 @@
1177 1222 );
1178 1223 }
1179 1224
1180 1225 /**
1226 + * Load wp-build with the screen ID aliased across its generated enqueue check.
1227 + *
1228 + * @see WP_Build_Screen_Id::load_with_alias()
1229 + * @return void
1230 + */
1231 + private static function load_wp_build_with_screen_alias() {
1232 + // Fallback: an older wp-build-polyfills under the jetpack-autoloader may predate load_with_alias().
1233 + if ( method_exists( \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Screen_Id::class, 'load_with_alias' ) ) {
1234 + \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Screen_Id::load_with_alias(
1235 + array( __CLASS__, 'alias_screen_id_for_wp_build' ),
1236 + array( __CLASS__, 'restore_screen_id_after_wp_build' ),
1237 + function () {
1238 + self::load_wp_build();
1239 + }
1240 + );
1241 + return;
1242 + }
1243 +
1244 + add_action( 'admin_enqueue_scripts', array( __CLASS__, 'alias_screen_id_for_wp_build' ) );
1245 + self::load_wp_build();
1246 + add_action( 'admin_enqueue_scripts', array( __CLASS__, 'restore_screen_id_after_wp_build' ) );
1247 + }
1248 +
1249 + /**
1181 1250 * Alias the current screen ID to satisfy wp-build's auto-generated enqueue check.
1182 1251 *
1183 1252 * Wp-build's `<page>-wp-admin` enqueue callback enqueues only when the screen ID
1184 1253 * matches the wp-build page slug (`jetpack-backup-dashboard`). Our WP-admin
@@ -1187,32 +1256,74 @@
1187 1256 *
1188 1257 * Hooked only when modernization is on AND we're on the Backup admin page,
1189 1258 * so this never affects any other request.
1190 1259 *
1191 - * @param \WP_Screen|null $screen The current screen object (passed by WP).
1260 + * @since 5.0.4 Takes no argument; hooked on `admin_enqueue_scripts`.
1261 + *
1192 1262 * @return void
1193 1263 */
1194 - public static function alias_screen_id_for_wp_build( $screen ) {
1195 - if ( ! is_object( $screen ) ) {
1264 + public static function alias_screen_id_for_wp_build() {
1265 + $screen = get_current_screen();
1266 + if ( ! $screen ) {
1196 1267 return;
1197 1268 }
1198 1269
1199 - $screen->id = 'jetpack-backup-dashboard';
1270 + self::$wp_build_original_screen_id = $screen->id;
1271 + $screen->id = 'jetpack-backup-dashboard';
1200 1272 }
1201 1273
1202 1274 /**
1203 - * Returns true when the wp-build modernization filter is enabled.
1275 + * Undo alias_screen_id_for_wp_build(), so code after the generated check sees the real screen ID.
1204 1276 *
1277 + * @since 5.0.4
1278 + *
1279 + * @return void
1280 + */
1281 + public static function restore_screen_id_after_wp_build() {
1282 + $screen = get_current_screen();
1283 + if ( ! $screen || null === self::$wp_build_original_screen_id ) {
1284 + return;
1285 + }
1286 +
1287 + $screen->id = self::$wp_build_original_screen_id;
1288 + self::$wp_build_original_screen_id = null;
1289 + }
1290 +
1291 + /**
1292 + * Returns the modernization filter's value, which defaults to the internal preview.
1293 + *
1205 1294 * @since 4.3.14 Changed from private to public; the REST bridges gate their route registration on it.
1206 1295 *
1207 1296 * @return bool
1208 1297 */
1209 1298 public static function is_modernized() {
1210 - return (bool) apply_filters( self::MODERNIZATION_FILTER, false );
1299 + return (bool) apply_filters( self::MODERNIZATION_FILTER, self::is_internal_preview() );
1211 1300 }
1212 1301
1213 1302 /**
1214 - * Returns true when the modernization filter is on AND the wp-build dashboard loaded.
1303 + * Whether an internal user on the A8C proxy previews the dashboard. Not an authorization check.
1304 + *
1305 + * The proxy is checked first, so other requests never make the connected-user lookup.
1306 + *
1307 + * @return bool
1308 + */
1309 + private static function is_internal_preview() {
1310 + if ( ! Constants::is_true( 'AT_PROXIED_REQUEST' ) ) {
1311 + return false;
1312 + }
1313 +
1314 + if ( function_exists( 'wpcomsh_is_site_sticker_active' ) && wpcomsh_is_site_sticker_active( self::LEGACY_DASHBOARD_STICKER ) ) {
1315 + return false;
1316 + }
1317 +
1318 + $user_data = ( new Connection_Manager() )->get_connected_user_data();
1319 + $email = is_array( $user_data ) && ! empty( $user_data['email'] ) ? strtolower( (string) $user_data['email'] ) : '';
1320 +
1321 + return str_ends_with( $email, '@automattic.com' ) || str_ends_with( $email, '@a8c.com' );
1322 + }
1323 +
1324 + /**
1325 + * Returns true when `is_modernized()` is true AND the wp-build dashboard loaded.
1215 1326 *
1216 1327 * `build/` is gitignored, so the render function is absent in any unbuilt checkout
1217 1328 * and in any release whose wp-build step failed. Every consumer of the modernized
1218 1329 * surface has to agree on this, or the menu falls back to the legacy page while the