2022-06-16 14:01:47 +02:00
< ? php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2023-05-23 23:18:12 +02:00
2025-02-28 08:42:11 +01:00
use Automattic\Jetpack\Admin_UI\Admin_Menu ;
2023-05-23 23:18:12 +02:00
use Automattic\Jetpack\Assets\Logo ;
2022-04-02 10:26:41 +02:00
use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State ;
2021-04-27 08:32:47 +02:00
use Automattic\Jetpack\Connection\Manager as Connection_Manager ;
2025-04-25 12:30:07 +02:00
use Automattic\Jetpack\Publicize\Publicize_Script_Data ;
2020-12-10 14:04:11 +01:00
use Automattic\Jetpack\Status ;
2020-01-26 12:53:20 +01:00
2022-04-02 10:26:41 +02:00
require_once __DIR__ . '/class.jetpack-admin-page.php' ;
2021-07-23 11:58:50 +02:00
require_once __DIR__ . '/class-jetpack-redux-state-helper.php' ;
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
/**
* Builds the landing page and its menu .
*/
2019-11-15 23:26:29 +01:00
class Jetpack_React_Page extends Jetpack_Admin_Page {
2022-06-16 14:01:47 +02:00
/**
* Show the landing page only when Jetpack is connected .
*
* @ var bool
*/
2019-11-15 23:26:29 +01:00
protected $dont_show_if_not_active = false ;
2022-06-16 14:01:47 +02:00
/**
* Used for fallback when REST API is disabled .
*
* @ var bool
*/
2019-11-15 23:26:29 +01:00
protected $is_redirecting = false ;
2022-06-16 14:01:47 +02:00
/**
* Add the main admin Jetpack menu .
*
* @ return string | false Return value from WordPress ' s `add_menu_page()` .
*/
public function get_page_hook () {
2023-05-23 23:18:12 +02:00
$icon = ( new Logo () ) -> get_base64_logo ();
return add_menu_page ( 'Jetpack' , 'Jetpack' , 'jetpack_admin_page' , 'jetpack' , array ( $this , 'render' ), $icon , 3 );
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
/**
* Add page action .
*
* @ param string $hook Hook of current page .
* @ return void
*/
public function add_page_actions ( $hook ) {
2022-04-02 10:26:41 +02:00
/** This action is documented in class.jetpack-admin.php */
2019-11-15 23:26:29 +01:00
do_action ( 'jetpack_admin_menu' , $hook );
2025-02-28 08:42:11 +01:00
if ( ! isset ( $_GET [ 'page' ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return ;
}
$page = sanitize_text_field ( wp_unslash ( $_GET [ 'page' ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'jetpack' !== $page ) {
if ( strpos ( $page , 'jetpack/' ) === 0 ) {
$section = substr ( $page , 8 );
wp_safe_redirect ( admin_url ( 'admin.php?page=jetpack#/' . $section ) );
exit ( 0 );
}
2022-06-16 14:01:47 +02:00
return ; // No need to handle the fallback redirection if we are not on the Jetpack page.
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
// Adding a redirect meta tag if the REST API is disabled.
2019-11-15 23:26:29 +01:00
if ( ! $this -> is_rest_api_enabled () ) {
$this -> is_redirecting = true ;
add_action ( 'admin_head' , array ( $this , 'add_fallback_head_meta' ) );
}
2022-06-16 14:01:47 +02:00
// Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled.
2019-11-15 23:26:29 +01:00
add_action ( 'admin_head' , array ( $this , 'add_noscript_head_meta' ) );
// If this is the first time the user is viewing the admin, don't show JITMs.
// This filter is added just in time because this function is called on admin_menu
2022-06-16 14:01:47 +02:00
// and JITMs are initialized on admin_init.
2021-04-27 08:32:47 +02:00
if ( Jetpack :: is_connection_ready () && ! Jetpack_Options :: get_option ( 'first_admin_view' , false ) ) {
2019-11-15 23:26:29 +01:00
Jetpack_Options :: update_option ( 'first_admin_view' , true );
add_filter ( 'jetpack_just_in_time_msgs' , '__return_false' );
}
}
2025-02-28 08:42:11 +01:00
/**
2025-05-07 06:48:41 +02:00
* Remove the main Jetpack submenu if a site is in offline mode or connected
* or if My Jetpack is available .
2025-02-28 08:42:11 +01:00
* At that point , admins can access the Jetpack Dashboard instead .
*
* @ since 13.8
*/
public function remove_jetpack_menu () {
2025-05-07 06:48:41 +02:00
$is_offline_mode = ( new Status () ) -> is_offline_mode ();
$has_my_jetpack = (
class_exists ( 'Automattic\Jetpack\My_Jetpack\Initializer' ) &&
method_exists ( 'Automattic\Jetpack\My_Jetpack\Initializer' , 'should_initialize' ) &&
\Automattic\Jetpack\My_Jetpack\Initializer :: should_initialize ()
);
if ( $is_offline_mode || $has_my_jetpack || Jetpack :: is_connection_ready () ) {
2025-02-28 08:42:11 +01:00
remove_submenu_page ( 'jetpack' , 'jetpack' );
}
}
2019-11-15 23:26:29 +01:00
/**
* Add Jetpack Dashboard sub - link and point it to AAG if the user can view stats , manage modules or if Protect is active .
*
* Works in Dev Mode or when user is connected .
*
* @ since 4.3 . 0
*/
2022-06-16 14:01:47 +02:00
public function jetpack_add_dashboard_sub_nav_item () {
2021-04-27 08:32:47 +02:00
if ( ( new Status () ) -> is_offline_mode () || Jetpack :: is_connection_ready () ) {
2025-02-28 08:42:11 +01:00
Admin_Menu :: add_menu (
__ ( 'Dashboard' , 'jetpack' ),
__ ( 'Dashboard' , 'jetpack' ),
'jetpack_admin_page' ,
Jetpack :: admin_url ( array ( 'page' => 'jetpack#/dashboard' ) ),
null , // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- See https://core.trac.wordpress.org/ticket/52539.
14
);
2019-11-15 23:26:29 +01:00
}
}
/**
2021-07-23 11:58:50 +02:00
* Determine whether a user can access the Jetpack Settings page .
*
* Rules are :
* - user is allowed to see the Jetpack Admin
* - site is connected or in offline mode
* - non - admins only need access to the settings when there are modules they can manage .
*
* @ return bool $can_access_settings Can the user access settings .
*/
private function can_access_settings () {
$connection = new Connection_Manager ( 'jetpack' );
$status = new Status ();
// User must have the necessary permissions to see the Jetpack settings pages.
if ( ! current_user_can ( 'edit_posts' ) ) {
return false ;
}
// In offline mode, allow access to admins.
if ( $status -> is_offline_mode () && current_user_can ( 'manage_options' ) ) {
return true ;
}
// If not in offline mode but site is not connected, bail.
if ( ! Jetpack :: is_connection_ready () ) {
return false ;
}
/*
* Additional checks for non - admins .
*/
if ( ! current_user_can ( 'manage_options' ) ) {
// If the site isn't connected at all, bail.
if ( ! $connection -> has_connected_owner () ) {
return false ;
}
/*
* If they haven ' t connected their own account yet ,
* they have no use for the settings page .
* They will not be able to manage any settings .
*/
if ( ! $connection -> is_user_connected () ) {
return false ;
}
/*
* Non - admins only have access to settings
* for the following modules :
* - Publicize
* - Post By Email
* If those modules are not available , bail .
*/
if (
! Jetpack :: is_module_active ( 'post-by-email' )
2025-04-25 12:30:07 +02:00
&& (
Publicize_Script_Data :: has_feature_flag ( 'admin-page' ) ||
! Jetpack :: is_module_active ( 'publicize' ) ||
! current_user_can ( 'publish_posts' )
)
2021-07-23 11:58:50 +02:00
) {
return false ;
}
}
// fallback.
return true ;
}
/**
* Jetpack Settings sub - link .
2019-11-15 23:26:29 +01:00
*
* @ since 4.3 . 0
2021-07-23 11:58:50 +02:00
* @ since 9.7 . 0 If Connection does not have an owner , restrict it to admins
2019-11-15 23:26:29 +01:00
*/
2022-06-16 14:01:47 +02:00
public function jetpack_add_settings_sub_nav_item () {
2021-07-23 11:58:50 +02:00
if ( $this -> can_access_settings () ) {
2025-02-28 08:42:11 +01:00
Admin_Menu :: add_menu (
__ ( 'Settings' , 'jetpack' ),
__ ( 'Settings' , 'jetpack' ),
'jetpack_admin_page' ,
Jetpack :: admin_url ( array ( 'page' => 'jetpack#/settings' ) ),
null , // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- See https://core.trac.wordpress.org/ticket/52539.
13
);
2019-11-15 23:26:29 +01:00
}
}
2022-06-16 14:01:47 +02:00
/**
* Fallback redirect meta tag if the REST API is disabled .
*
* @ return void
*/
public function add_fallback_head_meta () {
2019-11-15 23:26:29 +01:00
echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">' ;
}
2022-06-16 14:01:47 +02:00
/**
* Fallback meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled .
*
* @ return void
*/
public function add_noscript_head_meta () {
2019-11-15 23:26:29 +01:00
echo '<noscript>' ;
$this -> add_fallback_head_meta ();
echo '</noscript>' ;
}
2022-06-16 14:01:47 +02:00
/**
* Add action to render page specific HTML .
*
* @ return void
*/
public function page_render () {
/** This action is already documented in class.jetpack-admin-page.php */
2019-11-15 23:26:29 +01:00
do_action ( 'jetpack_notices' );
2022-06-16 14:01:47 +02:00
// Fetch static.html.
$static_html = @ file_get_contents ( JETPACK__PLUGIN_DIR . '_inc/build/static.html' ); //phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, Not fetching a remote file.
2019-11-15 23:26:29 +01:00
if ( false === $static_html ) {
2022-06-16 14:01:47 +02:00
// If we still have nothing, display an error.
2019-11-15 23:26:29 +01:00
echo '<p>' ;
esc_html_e ( 'Error fetching static.html. Try running: ' , 'jetpack' );
2022-06-16 14:01:47 +02:00
echo '<code>pnpm run distclean && pnpm jetpack build plugins/jetpack</code>' ;
2019-11-15 23:26:29 +01:00
echo '</p>' ;
} else {
2022-06-16 14:01:47 +02:00
// We got the static.html so let's display it.
echo $static_html ; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2019-11-15 23:26:29 +01:00
}
}
2021-09-07 21:40:48 +02:00
/**
* Allow robust deep links to React .
*
* The Jetpack dashboard requires fragments / hash values to make
* a deep link to it but passing fragments as part of a return URL
* will most often be discarded throughout the process .
* This logic aims to bridge this gap and reduce the chance of React
* specific links being broken while passing them along .
*/
public function react_redirects () {
global $pagenow ;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'admin.php' !== $pagenow || ! isset ( $_GET [ 'jp-react-redirect' ] ) ) {
return ;
}
$allowed_paths = array (
'product-purchased' => admin_url ( '/admin.php?page=jetpack#/recommendations/product-purchased' ),
);
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
2022-06-16 14:01:47 +02:00
$target = sanitize_text_field ( wp_unslash ( $_GET [ 'jp-react-redirect' ] ) );
2021-09-07 21:40:48 +02:00
if ( isset ( $allowed_paths [ $target ] ) ) {
wp_safe_redirect ( $allowed_paths [ $target ] );
2025-02-28 08:42:11 +01:00
exit ( 0 );
2021-09-07 21:40:48 +02:00
}
}
2022-06-16 14:01:47 +02:00
/**
* Load styles for static page .
*/
public function additional_styles () {
2019-11-15 23:26:29 +01:00
Jetpack_Admin_Page :: load_wrapper_styles ();
}
2022-06-16 14:01:47 +02:00
/**
* Load admin page scripts .
*/
public function page_admin_scripts () {
2019-11-15 23:26:29 +01:00
if ( $this -> is_redirecting ) {
2022-06-16 14:01:47 +02:00
return ; // No need for scripts on a fallback page.
2019-11-15 23:26:29 +01:00
}
2020-12-10 14:04:11 +01:00
$status = new Status ();
$is_offline_mode = $status -> is_offline_mode ();
$site_suffix = $status -> get_site_suffix ();
2020-01-26 12:53:20 +01:00
$script_deps_path = JETPACK__PLUGIN_DIR . '_inc/build/admin.asset.php' ;
2023-04-26 17:39:43 +02:00
$script_dependencies = array ( 'jquery' , 'wp-polyfill' );
2022-06-16 14:01:47 +02:00
$version = JETPACK__VERSION ;
2020-01-26 12:53:20 +01:00
if ( file_exists ( $script_deps_path ) ) {
$asset_manifest = include $script_deps_path ;
$script_dependencies = $asset_manifest [ 'dependencies' ];
2022-06-16 14:01:47 +02:00
$version = $asset_manifest [ 'version' ];
2019-11-15 23:26:29 +01:00
}
2024-02-16 11:03:01 +01:00
$blog_id_prop = '' ;
if ( ! defined ( 'IS_WPCOM' ) || ! IS_WPCOM ) {
$blog_id = Connection_Manager :: get_site_id ( true );
if ( $blog_id ) {
$blog_id_prop = ', currentBlogID: "' . ( int ) $blog_id . '"' ;
}
}
2020-01-26 12:53:20 +01:00
wp_enqueue_script (
'react-plugin' ,
plugins_url ( '_inc/build/admin.js' , JETPACK__PLUGIN_FILE ),
$script_dependencies ,
2022-06-16 14:01:47 +02:00
$version ,
2020-01-26 12:53:20 +01:00
true
);
2019-11-15 23:26:29 +01:00
2021-04-27 08:32:47 +02:00
if ( ! $is_offline_mode && Jetpack :: is_connection_ready () ) {
2019-11-15 23:26:29 +01:00
// Required for Analytics.
wp_enqueue_script ( 'jp-tracks' , '//stats.wp.com/w.js' , array (), gmdate ( 'YW' ), true );
}
2020-09-15 14:30:05 +02:00
wp_set_script_translations ( 'react-plugin' , 'jetpack' );
2019-11-15 23:26:29 +01:00
// Add objects to be passed to the initial state of the app.
2020-05-06 17:20:49 +02:00
// Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
2021-07-23 11:58:50 +02:00
wp_add_inline_script ( 'react-plugin' , 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode ( wp_json_encode ( Jetpack_Redux_State_Helper :: get_initial_state () ) ) . '"));' , 'before' );
2020-11-18 09:10:44 +01:00
// This will set the default URL of the jp_redirects lib.
2024-02-16 11:03:01 +01:00
wp_add_inline_script ( 'react-plugin' , 'var jetpack_redirects = { currentSiteRawUrl: "' . $site_suffix . '"' . $blog_id_prop . ' };' , 'before' );
2022-04-02 10:26:41 +02:00
// Adds Connection package initial state.
2023-09-26 10:24:36 +02:00
Connection_Initial_State :: render_script ( 'react-plugin' );
2019-11-15 23:26:29 +01:00
}
}