← 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 |