Add upstream

This commit is contained in:
root
2019-10-24 00:12:05 +02:00
parent 85d41e4216
commit ac980f592c
3504 changed files with 1049983 additions and 29971 deletions

View File

@@ -0,0 +1,24 @@
<?php
/**
* Loading the various functions used for Jetpack Debugging.
*
* @package Jetpack.
*/
global $wp_version;
/* Jetpack Connection Testing Framework */
require_once 'class-jetpack-cxn-test-base.php';
/* Jetpack Connection Tests */
require_once 'class-jetpack-cxn-tests.php';
/* Jetpack Debug Data */
require_once 'class-jetpack-debug-data.php';
/* The "In-Plugin Debugger" admin page. */
require_once 'class-jetpack-debugger.php';
if ( version_compare( $wp_version, '5.2-alpha', 'ge' ) ) {
require_once 'debug-functions-for-php53.php';
add_filter( 'debug_information', array( 'Jetpack_Debug_Data', 'core_debug_data' ) );
add_filter( 'site_status_tests', 'jetpack_debugger_site_status_tests' );
add_action( 'wp_ajax_health-check-jetpack-local_testing_suite', 'jetpack_debugger_ajax_local_testing_suite' );
}

View File

@@ -0,0 +1,471 @@
<?php
/**
* Jetpack Connection Testing
*
* Framework for various "unit tests" against the Jetpack connection.
*
* Individual tests should be added to the class-jetpack-cxn-tests.php file.
*
* @author Brandon Kraft
* @package Jetpack
*/
/**
* "Unit Tests" for the Jetpack connection.
*
* @since 7.1.0
*/
class Jetpack_Cxn_Test_Base {
/**
* Tests to run on the Jetpack connection.
*
* @var array $tests
*/
protected $tests = array();
/**
* Results of the Jetpack connection tests.
*
* @var array $results
*/
protected $results = array();
/**
* Status of the testing suite.
*
* Used internally to determine if a test should be skipped since the tests are already failing. Assume passing.
*
* @var bool $pass
*/
protected $pass = true;
/**
* Jetpack_Cxn_Test constructor.
*/
public function __construct() {
$this->tests = array();
$this->results = array();
}
/**
* Adds a new test to the Jetpack Connection Testing suite.
*
* @since 7.1.0
* @since 7.3.0 Adds name parameter and returns WP_Error on failure.
*
* @param callable $callable Test to add to queue.
* @param string $name Unique name for the test.
* @param string $type Optional. Core Site Health type: 'direct' if test can be run during initial load or 'async' if test should run async.
* @param array $groups Optional. Testing groups to add test to.
*
* @return mixed True if successfully added. WP_Error on failure.
*/
public function add_test( $callable, $name, $type = 'direct', $groups = array( 'default' ) ) {
if ( is_array( $name ) ) {
// Pre-7.3.0 method passed the $groups parameter here.
return new WP_Error( __( 'add_test arguments changed in 7.3.0. Please reference inline documentation.', 'jetpack' ) );
}
if ( array_key_exists( $name, $this->tests ) ) {
return new WP_Error( __( 'Test names must be unique.', 'jetpack' ) );
}
if ( ! is_callable( $callable ) ) {
return new WP_Error( __( 'Tests must be valid PHP callables.', 'jetpack' ) );
}
$this->tests[ $name ] = array(
'name' => $name,
'test' => $callable,
'group' => $groups,
'type' => $type,
);
return true;
}
/**
* Lists all tests to run.
*
* @since 7.3.0
*
* @param string $type Optional. Core Site Health type: 'direct' or 'async'. All by default.
* @param string $group Optional. A specific testing group. All by default.
*
* @return array $tests Array of tests with test information.
*/
public function list_tests( $type = 'all', $group = 'all' ) {
if ( ! ( 'all' === $type || 'direct' === $type || 'async' === $type ) ) {
_doing_it_wrong( 'Jetpack_Cxn_Test_Base->list_tests', 'Type must be all, direct, or async', '7.3.0' );
}
$tests = array();
foreach ( $this->tests as $name => $value ) {
// Get all valid tests by group staged.
if ( 'all' === $group || $group === $value['group'] ) {
$tests[ $name ] = $value;
}
// Next filter out any that do not match the type.
if ( 'all' !== $type && $type !== $value['type'] ) {
unset( $tests[ $name ] );
}
}
return $tests;
}
/**
* Run a specific test.
*
* @since 7.3.0
*
* @param string $name Name of test.
*
* @return mixed $result Test result array or WP_Error if invalid name. {
* @type string $name Test name
* @type mixed $pass True if passed, false if failed, 'skipped' if skipped.
* @type string $message Human-readable test result message.
* @type string $resolution Human-readable resolution steps.
* }
*/
public function run_test( $name ) {
if ( array_key_exists( $name, $this->tests ) ) {
return call_user_func( $this->tests[ $name ]['test'] );
}
return new WP_Error( __( 'There is no test by that name: ', 'jetpack' ) . $name );
}
/**
* Runs the Jetpack connection suite.
*/
public function run_tests() {
foreach ( $this->tests as $test ) {
$result = call_user_func( $test['test'] );
$result['group'] = $test['group'];
$result['type'] = $test['type'];
$this->results[] = $result;
if ( false === $result['pass'] ) {
$this->pass = false;
}
}
}
/**
* Returns the full results array.
*
* @since 7.1.0
* @since 7.3.0 Add 'type'
*
* @param string $type Test type, async or direct.
* @param string $group Testing group whose results we want. Defaults to all tests.
* @return array Array of test results.
*/
public function raw_results( $type = 'all', $group = 'all' ) {
if ( ! $this->results ) {
$this->run_tests();
}
$results = $this->results;
if ( 'all' !== $group ) {
foreach ( $results as $test => $result ) {
if ( ! in_array( $group, $result['group'], true ) ) {
unset( $results[ $test ] );
}
}
}
if ( 'all' !== $type ) {
foreach ( $results as $test => $result ) {
if ( $type !== $result['type'] ) {
unset( $results[ $test ] );
}
}
}
return $results;
}
/**
* Returns the status of the connection suite.
*
* @since 7.1.0
* @since 7.3.0 Add 'type'
*
* @param string $type Test type, async or direct. Optional, direct all tests.
* @param string $group Testing group to check status of. Optional, default all tests.
*
* @return true|array True if all tests pass. Array of failed tests.
*/
public function pass( $type = 'all', $group = 'all' ) {
$results = $this->raw_results( $type, $group );
foreach ( $results as $result ) {
// 'pass' could be true, false, or 'skipped'. We only want false.
if ( isset( $result['pass'] ) && false === $result['pass'] ) {
return false;
}
}
return true;
}
/**
* Return array of failed test messages.
*
* @since 7.1.0
* @since 7.3.0 Add 'type'
*
* @param string $type Test type, direct or async.
* @param string $group Testing group whose failures we want. Defaults to "all".
*
* @return false|array False if no failed tests. Otherwise, array of failed tests.
*/
public function list_fails( $type = 'all', $group = 'all' ) {
$results = $this->raw_results( $type, $group );
foreach ( $results as $test => $result ) {
// We do not want tests that passed or ones that are misconfigured (no pass status or no failure message).
if ( ! isset( $result['pass'] ) || false !== $result['pass'] || ! isset( $result['message'] ) ) {
unset( $results[ $test ] );
}
}
return $results;
}
/**
* Helper function to return consistent responses for a passing test.
*
* @param string $name Test name.
*
* @return array Test results.
*/
public static function passing_test( $name = 'Unnamed' ) {
return array(
'name' => $name,
'pass' => true,
'message' => __( 'Test Passed!', 'jetpack' ),
'resolution' => false,
'severity' => false,
);
}
/**
* Helper function to return consistent responses for a skipped test.
*
* @param string $name Test name.
* @param string $message Reason for skipping the test. Optional.
*
* @return array Test results.
*/
public static function skipped_test( $name = 'Unnamed', $message = false ) {
return array(
'name' => $name,
'pass' => 'skipped',
'message' => $message,
'resolution' => false,
'severity' => false,
);
}
/**
* Helper function to return consistent responses for a failing test.
*
* @since 7.1.0
* @since 7.3.0 Added $action for resolution action link, $severity for issue severity.
*
* @param string $name Test name.
* @param string $message Message detailing the failure.
* @param string $resolution Optional. Steps to resolve.
* @param string $action Optional. URL to direct users to self-resolve.
* @param string $severity Optional. "critical" or "recommended" for failure stats. "good" for passing.
*
* @return array Test results.
*/
public static function failing_test( $name, $message, $resolution = false, $action = false, $severity = 'critical' ) {
// Provide standard resolutions steps, but allow pass-through of non-standard ones.
switch ( $resolution ) {
case 'cycle_connection':
$resolution = __( 'Please disconnect and reconnect Jetpack.', 'jetpack' ); // @todo: Link.
break;
case 'outbound_requests':
$resolution = __( 'Please ask your hosting provider to confirm your server can make outbound requests to jetpack.com.', 'jetpack' );
break;
case 'support':
case false:
$resolution = __( 'Please contact Jetpack support.', 'jetpack' ); // @todo: Link to support.
break;
}
return array(
'name' => $name,
'pass' => false,
'message' => $message,
'resolution' => $resolution,
'action' => $action,
'severity' => $severity,
);
}
/**
* Provide WP_CLI friendly testing results.
*
* @since 7.1.0
* @since 7.3.0 Add 'type'
*
* @param string $type Test type, direct or async.
* @param string $group Testing group whose results we are outputting. Default all tests.
*/
public function output_results_for_cli( $type = 'all', $group = 'all' ) {
if ( defined( 'WP_CLI' ) && WP_CLI ) {
if ( Jetpack::is_development_mode() ) {
WP_CLI::line( __( 'Jetpack is in Development Mode:', 'jetpack' ) );
WP_CLI::line( Jetpack::development_mode_trigger_text() );
}
WP_CLI::line( __( 'TEST RESULTS:', 'jetpack' ) );
foreach ( $this->raw_results( $group ) as $test ) {
if ( true === $test['pass'] ) {
WP_CLI::log( WP_CLI::colorize( '%gPassed:%n ' . $test['name'] ) );
} elseif ( 'skipped' === $test['pass'] ) {
WP_CLI::log( WP_CLI::colorize( '%ySkipped:%n ' . $test['name'] ) );
if ( $test['message'] ) {
WP_CLI::log( ' ' . $test['message'] ); // Number of spaces to "tab indent" the reason.
}
} else { // Failed.
WP_CLI::log( WP_CLI::colorize( '%rFailed:%n ' . $test['name'] ) );
WP_CLI::log( ' ' . $test['message'] ); // Number of spaces to "tab indent" the reason.
}
}
}
}
/**
* Output results of failures in format expected by Core's Site Health tool for async tests.
*
* Specifically not asking for a testing group since we're opinionated that Site Heath should see all.
*
* @since 7.3.0
*
* @return array Array of test results
*/
public function output_results_for_core_async_site_health() {
$result = array(
'label' => __( 'Jetpack passed all async tests.', 'jetpack' ),
'status' => 'good',
'badge' => array(
'label' => __( 'Jetpack', 'jetpack' ),
'color' => 'green',
),
'description' => sprintf(
'<p>%s</p>',
__( "Jetpack's async local testing suite passed all tests!", 'jetpack' )
),
'actions' => '',
'test' => 'jetpack_debugger_local_testing_suite_core',
);
if ( $this->pass() ) {
return $result;
}
$fails = $this->list_fails( 'async' );
$error = false;
foreach ( $fails as $fail ) {
if ( ! $error ) {
$error = true;
$result['label'] = $fail['message'];
$result['status'] = $fail['severity'];
$result['description'] = sprintf(
'<p>%s</p>',
$fail['resolution']
);
if ( ! empty( $fail['action'] ) ) {
$result['actions'] = sprintf(
'<a class="button button-primary" href="%1$s" target="_blank" rel="noopener noreferrer">%2$s <span class="screen-reader-text">%3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>',
esc_url( $fail['action'] ),
__( 'Resolve', 'jetpack' ),
/* translators: accessibility text */
__( '(opens in a new tab)', 'jetpack' )
);
}
} else {
$result['description'] .= sprintf(
'<p>%s</p>',
__( 'There was another problem:', 'jetpack' )
) . ' ' . $fail['message'] . ': ' . $fail['resolution'];
if ( 'critical' === $fail['severity'] ) { // In case the initial failure is only "recommended".
$result['status'] = 'critical';
}
}
}
return $result;
}
/**
* Provide single WP Error instance of all failures.
*
* @since 7.1.0
* @since 7.3.0 Add 'type'
*
* @param string $type Test type, direct or async.
* @param string $group Testing group whose failures we want converted. Default all tests.
*
* @return WP_Error|false WP_Error with all failed tests or false if there were no failures.
*/
public function output_fails_as_wp_error( $type = 'all', $group = 'all' ) {
if ( $this->pass( $group ) ) {
return false;
}
$fails = $this->list_fails( $type, $group );
$error = false;
foreach ( $fails as $result ) {
$code = 'failed_' . $result['name'];
$message = $result['message'];
$data = array(
'resolution' => $result['resolution'],
);
if ( ! $error ) {
$error = new WP_Error( $code, $message, $data );
} else {
$error->add( $code, $message, $data );
}
}
return $error;
}
/**
* Encrypt data for sending to WordPress.com.
*
* @todo When PHP minimum is 5.3+, add cipher detection to use an agreed better cipher than RC4. RC4 should be the last resort.
*
* @param string $data Data to encrypt with the WP.com Public Key.
*
* @return false|array False if functionality not available. Array of encrypted data, encryption key.
*/
public function encrypt_string_for_wpcom( $data ) {
$return = false;
if ( ! function_exists( 'openssl_get_publickey' ) || ! function_exists( 'openssl_seal' ) ) {
return $return;
}
$public_key = openssl_get_publickey( JETPACK__DEBUGGER_PUBLIC_KEY );
if ( $public_key && openssl_seal( $data, $encrypted_data, $env_key, array( $public_key ) ) ) {
// We are returning base64-encoded values to ensure they're characters we can use in JSON responses without issue.
$return = array(
'data' => base64_encode( $encrypted_data ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
'key' => base64_encode( $env_key[0] ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
'cipher' => 'RC4', // When Jetpack's minimum WP version is at PHP 5.3+, we will add in detecting and using a stronger one.
);
}
openssl_free_key( $public_key );
return $return;
}
}

View File

@@ -0,0 +1,346 @@
<?php
/**
* Collection of tests to run on the Jetpack connection locally.
*
* @package Jetpack
*/
use Automattic\Jetpack\Connection\Client;
/**
* Class Jetpack_Cxn_Tests contains all of the actual tests.
*/
class Jetpack_Cxn_Tests extends Jetpack_Cxn_Test_Base {
/**
* Jetpack_Cxn_Tests constructor.
*/
public function __construct() {
parent::__construct();
$methods = get_class_methods( 'Jetpack_Cxn_Tests' );
foreach ( $methods as $method ) {
if ( false === strpos( $method, 'test__' ) ) {
continue;
}
$this->add_test( array( $this, $method ), $method, 'direct' );
}
/**
* Fires after loading default Jetpack Connection tests.
*
* @since 7.1.0
*/
do_action( 'jetpack_connection_tests_loaded' );
/**
* Determines if the WP.com testing suite should be included.
*
* @since 7.1.0
*
* @param bool $run_test To run the WP.com testing suite. Default true.
*/
if ( apply_filters( 'jetpack_debugger_run_self_test', true ) ) {
/**
* Intentionally added last as it checks for an existing failure state before attempting.
* Generally, any failed location condition would result in the WP.com check to fail too, so
* we will skip it to avoid confusing error messages.
*
* Note: This really should be an 'async' test.
*/
$this->add_test( array( $this, 'last__wpcom_self_test' ), 'test__wpcom_self_test', 'direct' );
}
}
/**
* Helper function to look up the expected master user and return the local WP_User.
*
* @return WP_User Jetpack's expected master user.
*/
protected function helper_retrieve_local_master_user() {
$master_user = Jetpack_Options::get_option( 'master_user' );
return new WP_User( $master_user );
}
/**
* Is Jetpack even connected and supposed to be talking to WP.com?
*/
protected function helper_is_jetpack_connected() {
return ( Jetpack::is_active() && ! Jetpack::is_development_mode() );
}
/**
* Test if Jetpack is connected.
*/
protected function test__check_if_connected() {
$name = __FUNCTION__;
if ( $this->helper_is_jetpack_connected() ) {
$result = self::passing_test( $name );
} elseif ( Jetpack::is_development_mode() ) {
$result = self::skipped_test( $name, __( 'Jetpack is in Development Mode:', 'jetpack' ) . ' ' . Jetpack::development_mode_trigger_text(), __( 'Disable development mode.', 'jetpack' ) );
} else {
$result = self::failing_test( $name, __( 'Jetpack is not connected.', 'jetpack' ), 'cycle_connection' );
}
return $result;
}
/**
* Test that the master user still exists on this site.
*
* @return array Test results.
*/
protected function test__master_user_exists_on_site() {
$name = __FUNCTION__;
if ( ! $this->helper_is_jetpack_connected() ) {
return self::skipped_test( $name, __( 'Jetpack is not connected. No master user to check.', 'jetpack' ) ); // Skip test.
}
$local_user = $this->helper_retrieve_local_master_user();
if ( $local_user->exists() ) {
$result = self::passing_test( $name );
} else {
$result = self::failing_test( $name, __( 'The user who setup the Jetpack connection no longer exists on this site.', 'jetpack' ), 'cycle_connection' );
}
return $result;
}
/**
* Test that the master user has the manage options capability (e.g. is an admin).
*
* Generic calls from WP.com execute on Jetpack as the master user. If it isn't an admin, random things will fail.
*
* @return array Test results.
*/
protected function test__master_user_can_manage_options() {
$name = __FUNCTION__;
if ( ! $this->helper_is_jetpack_connected() ) {
return self::skipped_test( $name, __( 'Jetpack is not connected.', 'jetpack' ) ); // Skip test.
}
$master_user = $this->helper_retrieve_local_master_user();
if ( user_can( $master_user, 'manage_options' ) ) {
$result = self::passing_test( $name );
} else {
/* translators: a WordPress username */
$result = self::failing_test( $name, sprintf( __( 'The user (%s) who setup the Jetpack connection is not an administrator.', 'jetpack' ), $master_user->user_login ), __( 'Either upgrade the user or disconnect and reconnect Jetpack.', 'jetpack' ) ); // @todo: Link to the right places.
}
return $result;
}
/**
* Test that the PHP's XML library is installed.
*
* While it should be installed by default, increasingly in PHP 7, some OSes require an additional php-xml package.
*
* @return array Test results.
*/
protected function test__xml_parser_available() {
$name = __FUNCTION__;
if ( function_exists( 'xml_parser_create' ) ) {
$result = self::passing_test( $name );
} else {
$result = self::failing_test( $name, __( 'PHP XML manipluation libraries are not available.', 'jetpack' ), __( "Please ask your hosting provider to refer to our server requirements at https://jetpack.com/support/server-requirements/ and enable PHP's XML module.", 'jetpack' ) );
}
return $result;
}
/**
* Test that the server is able to send an outbound http communication.
*
* @return array Test results.
*/
protected function test__outbound_http() {
$name = __FUNCTION__;
$request = wp_remote_get( preg_replace( '/^https:/', 'http:', JETPACK__API_BASE ) . 'test/1/' );
$code = wp_remote_retrieve_response_code( $request );
if ( 200 === intval( $code ) ) {
$result = self::passing_test( $name );
} else {
$result = self::failing_test( $name, __( 'Your server did not successfully connect to the Jetpack server using HTTP', 'jetpack' ), 'outbound_requests' );
}
return $result;
}
/**
* Test that the server is able to send an outbound https communication.
*
* @return array Test results.
*/
protected function test__outbound_https() {
$name = __FUNCTION__;
$request = wp_remote_get( preg_replace( '/^http:/', 'https:', JETPACK__API_BASE ) . 'test/1/' );
$code = wp_remote_retrieve_response_code( $request );
if ( 200 === intval( $code ) ) {
$result = self::passing_test( $name );
} else {
$result = self::failing_test( $name, __( 'Your server did not successfully connect to the Jetpack server using HTTPS', 'jetpack' ), 'outbound_requests' );
}
return $result;
}
/**
* Check for an IDC.
*
* @return array Test results.
*/
protected function test__identity_crisis() {
$name = __FUNCTION__;
if ( ! $this->helper_is_jetpack_connected() ) {
return self::skipped_test( $name, __( 'Jetpack is not connected.', 'jetpack' ) ); // Skip test.
}
$identity_crisis = Jetpack::check_identity_crisis();
if ( ! $identity_crisis ) {
$result = self::passing_test( $name );
} else {
$message = sprintf(
/* translators: Two URLs. The first is the locally-recorded value, the second is the value as recorded on WP.com. */
__( 'Your url is set as `%1$s`, but your WordPress.com connection lists it as `%2$s`!', 'jetpack' ),
$identity_crisis['home'],
$identity_crisis['wpcom_home']
);
$result = self::failing_test( $name, $message, 'support' );
}
return $result;
}
/**
* Tests connection status against wp.com's test-connection endpoint
*
* @todo: Compare with the wpcom_self_test. We only need one of these.
*
* @return array Test results.
*/
protected function test__wpcom_connection_test() {
$name = __FUNCTION__;
if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || Jetpack::is_staging_site() || ! $this->pass ) {
return self::skipped_test( $name );
}
$response = Client::wpcom_json_api_request_as_blog(
sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
Client::WPCOM_JSON_API_VERSION
);
if ( is_wp_error( $response ) ) {
/* translators: %1$s is the error code, %2$s is the error message */
$message = sprintf( __( 'Connection test failed (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() );
return self::failing_test( $name, $message );
}
$body = wp_remote_retrieve_body( $response );
if ( ! $body ) {
$message = __( 'Connection test failed (empty response body)', 'jetpack' ) . wp_remote_retrieve_response_code( $response );
return self::failing_test( $name, $message );
}
if ( 404 === wp_remote_retrieve_response_code( $response ) ) {
return self::skipped_test( $name, __( 'The WordPress.com API returned a 404 error.', 'jetpack' ) );
}
$result = json_decode( $body );
$is_connected = (bool) $result->connected;
$message = $result->message . ': ' . wp_remote_retrieve_response_code( $response );
if ( $is_connected ) {
return self::passing_test( $name );
} else {
return self::failing_test( $name, $message );
}
}
/**
* Tests the port number to ensure it is an expected value.
*
* We expect that sites on be on one of:
* port 80,
* port 443 (https sites only),
* the value of JETPACK_SIGNATURE__HTTP_PORT,
* unless the site is intentionally on a different port (e.g. example.com:8080 is the site's URL).
*
* If the value isn't one of those and the site's URL doesn't include a port, then the signature verification will fail.
*
* This happens most commonly on sites with reverse proxies, so the edge (e.g. Varnish) is running on 80/443, but nginx
* or Apache is responding internally on a different port (e.g. 81).
*
* @return array Test results
*/
protected function test__server_port_value() {
$name = __FUNCTION__;
if ( ! isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) && ! isset( $_SERVER['SERVER_PORT'] ) ) {
$message = 'The server port values are not defined. This is most common when running PHP via a CLI.';
return self::skipped_test( $name, $message );
}
$site_port = wp_parse_url( home_url(), PHP_URL_PORT );
$server_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? (int) $_SERVER['HTTP_X_FORWARDED_PORT'] : (int) $_SERVER['SERVER_PORT'];
$http_ports = array( 80 );
$https_ports = array( 80, 443 );
if ( defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ) {
$http_ports[] = JETPACK_SIGNATURE__HTTP_PORT;
}
if ( defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ) {
$https_ports[] = JETPACK_SIGNATURE__HTTPS_PORT;
}
if ( $site_port ) {
return self::skipped_test( $name ); // Not currently testing for this situation.
}
if ( is_ssl() && in_array( $server_port, $https_ports, true ) ) {
return self::passing_test( $name );
} elseif ( in_array( $server_port, $http_ports, true ) ) {
return self::passing_test( $name );
} else {
if ( is_ssl() ) {
$needed_constant = 'JETPACK_SIGNATURE__HTTPS_PORT';
} else {
$needed_constant = 'JETPACK_SIGNATURE__HTTP_PORT';
}
$message = __( 'The server port value is unexpected.', 'jetpack' );
$resolution = __( 'Try adding the following to your wp-config.php file:', 'jetpack' ) . " define( '$needed_constant', $server_port );";
return self::failing_test( $name, $message, $resolution );
}
}
/**
* Calls to WP.com to run the connection diagnostic testing suite.
*
* Intentionally added last as it will be skipped if any local failed conditions exist.
*
* @return array Test results.
*/
protected function last__wpcom_self_test() {
$name = 'test__wpcom_self_test';
if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || Jetpack::is_staging_site() || ! $this->pass ) {
return self::skipped_test( $name );
}
$self_xml_rpc_url = site_url( 'xmlrpc.php' );
$testsite_url = Jetpack::fix_url_for_bad_hosts( JETPACK__API_BASE . 'testsite/1/?url=' );
add_filter( 'http_request_timeout', array( 'Jetpack_Debugger', 'jetpack_increase_timeout' ) );
$response = wp_remote_get( $testsite_url . $self_xml_rpc_url );
remove_filter( 'http_request_timeout', array( 'Jetpack_Debugger', 'jetpack_increase_timeout' ) );
if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
return self::passing_test( $name );
} else {
return self::failing_test( $name, __( 'Jetpack.com detected an error.', 'jetpack' ), __( 'Visit the Jetpack.com debugging page for more information or contact support.', 'jetpack' ) ); // @todo direct links.
}
}
}

View File

@@ -0,0 +1,397 @@
<?php
/**
* Jetpack Debug Data for the legacy Jetpack debugger page and the WP 5.2-era Site Health sections.
*
* @package jetpack
*/
use Automattic\Jetpack\Constants;
use Automattic\Jetpack\Sync\Modules;
use Automattic\Jetpack\Sync\Functions;
use Automattic\Jetpack\Sync\Sender;
/**
* Class Jetpack_Debug_Data
*
* Collect and return debug data for Jetpack.
*
* @since 7.3.0
*/
class Jetpack_Debug_Data {
/**
* Determine the active plan and normalize it for the debugger results.
*
* @since 7.3.0
*
* @return string The plan slug.
*/
public static function what_jetpack_plan() {
$plan = Jetpack_Plan::get();
return ! empty( $plan['class'] ) ? $plan['class'] : 'undefined';
}
/**
* Convert seconds to human readable time.
*
* A dedication function instead of using Core functionality to allow for output in seconds.
*
* @since 7.3.0
*
* @param int $seconds Number of seconds to convert to human time.
*
* @return string Human readable time.
*/
public static function seconds_to_time( $seconds ) {
$seconds = intval( $seconds );
$units = array(
'week' => WEEK_IN_SECONDS,
'day' => DAY_IN_SECONDS,
'hour' => HOUR_IN_SECONDS,
'minute' => MINUTE_IN_SECONDS,
'second' => 1,
);
// specifically handle zero.
if ( 0 === $seconds ) {
return '0 seconds';
}
$human_readable = '';
foreach ( $units as $name => $divisor ) {
$quot = intval( $seconds / $divisor );
if ( $quot ) {
$human_readable .= "$quot $name";
$human_readable .= ( abs( $quot ) > 1 ? 's' : '' ) . ', ';
$seconds -= $quot * $divisor;
}
}
return substr( $human_readable, 0, -2 );
}
/**
* Return debug data in the format expected by Core's Site Health Info tab.
*
* @since 7.3.0
*
* @param array $debug {
* The debug information already compiled by Core.
*
* @type string $label The title for this section of the debug output.
* @type string $description Optional. A description for your information section which may contain basic HTML
* markup: `em`, `strong` and `a` for linking to documentation or putting emphasis.
* @type boolean $show_count Optional. If set to `true` the amount of fields will be included in the title for
* this section.
* @type boolean $private Optional. If set to `true` the section and all associated fields will be excluded
* from the copy-paste text area.
* @type array $fields {
* An associative array containing the data to be displayed.
*
* @type string $label The label for this piece of information.
* @type string $value The output that is of interest for this field.
* @type boolean $private Optional. If set to `true` the field will not be included in the copy-paste text area
* on top of the page, allowing you to show, for example, API keys here.
* }
* }
*
* @return array $args Debug information in the same format as the initial argument.
*/
public static function core_debug_data( $debug ) {
$support_url = Jetpack::is_development_version()
? 'https://jetpack.com/contact-support/beta-group/'
: 'https://jetpack.com/contact-support/';
$jetpack = array(
'jetpack' => array(
'label' => __( 'Jetpack', 'jetpack' ),
'description' => sprintf(
/* translators: %1$s is URL to jetpack.com's contact support page. %2$s accessibility text */
__(
'Diagnostic information helpful to <a href="%1$s" target="_blank" rel="noopener noreferrer">your Jetpack Happiness team<span class="screen-reader-text">%2$s</span></a>',
'jetpack'
),
esc_url( $support_url ),
__( '(opens in a new tab)', 'jetpack' )
),
'fields' => self::debug_data(),
),
);
$debug = array_merge( $debug, $jetpack );
return $debug;
}
/**
* Compile and return array of debug information.
*
* @since 7.3.0
*
* @return array $args {
* Associated array of arrays with the following.
* @type string $label The label for this piece of information.
* @type string $value The output that is of interest for this field.
* @type boolean $private Optional. Set to true if data is sensitive (API keys, etc).
* }
*/
public static function debug_data() {
$debug_info = array();
/* Add various important Jetpack options */
$debug_info['site_id'] = array(
'label' => 'Jetpack Site ID',
'value' => Jetpack_Options::get_option( 'id' ),
'private' => false,
);
$debug_info['ssl_cert'] = array(
'label' => 'Jetpack SSL Verfication Bypass',
'value' => ( Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) ? 'Yes' : 'No',
'private' => false,
);
$debug_info['time_diff'] = array(
'label' => "Offset between Jetpack server's time and this server's time.",
'value' => Jetpack_Options::get_option( 'time_diff' ),
'private' => false,
);
$debug_info['version_option'] = array(
'label' => 'Current Jetpack Version Option',
'value' => Jetpack_Options::get_option( 'version' ),
'private' => false,
);
$debug_info['old_version'] = array(
'label' => 'Previous Jetpack Version',
'value' => Jetpack_Options::get_option( 'old_version' ),
'private' => false,
);
$debug_info['public'] = array(
'label' => 'Jetpack Site Public',
'value' => ( Jetpack_Options::get_option( 'public' ) ) ? 'Public' : 'Private',
'private' => false,
);
$debug_info['master_user'] = array(
'label' => 'Jetpack Master User',
'value' => self::human_readable_master_user(),
'private' => false,
);
/**
* Token information is private, but awareness if there one is set is helpful.
*
* To balance out information vs privacy, we only display and include the "key",
* which is a segment of the token prior to a period within the token and is
* technically not private.
*
* If a token does not contain a period, then it is malformed and we report it as such.
*/
$user_id = get_current_user_id();
$blog_token = Jetpack_Data::get_access_token();
$user_token = Jetpack_Data::get_access_token( $user_id );
$tokenset = '';
if ( $blog_token ) {
$tokenset = 'Blog ';
$blog_key = substr( $blog_token->secret, 0, strpos( $blog_token->secret, '.' ) );
// Intentionally not translated since this is helpful when sent to Happiness.
$blog_key = ( $blog_key ) ? $blog_key : 'Potentially Malformed Token.';
}
if ( $user_token ) {
$tokenset .= 'User';
$user_key = substr( $user_token->secret, 0, strpos( $user_token->secret, '.' ) );
// Intentionally not translated since this is helpful when sent to Happiness.
$user_key = ( $user_key ) ? $user_key : 'Potentially Malformed Token.';
}
if ( ! $tokenset ) {
$tokenset = 'None';
}
$debug_info['current_user'] = array(
'label' => 'Current User',
'value' => self::human_readable_user( $user_id ),
'private' => false,
);
$debug_info['tokens_set'] = array(
'label' => 'Tokens defined',
'value' => $tokenset,
'private' => false,
);
$debug_info['blog_token'] = array(
'label' => 'Blog Public Key',
'value' => ( $blog_token ) ? $blog_key : 'Not set.',
'private' => false,
);
$debug_info['user_token'] = array(
'label' => 'User Public Key',
'value' => ( $user_token ) ? $user_key : 'Not set.',
'private' => false,
);
/** Jetpack Environmental Information */
$debug_info['version'] = array(
'label' => 'Jetpack Version',
'value' => JETPACK__VERSION,
'private' => false,
);
$debug_info['jp_plugin_dir'] = array(
'label' => 'Jetpack Directory',
'value' => JETPACK__PLUGIN_DIR,
'private' => false,
);
$debug_info['plan'] = array(
'label' => 'Plan Type',
'value' => self::what_jetpack_plan(),
'private' => false,
);
foreach ( array(
'HTTP_HOST',
'SERVER_PORT',
'HTTPS',
'GD_PHP_HANDLER',
'HTTP_AKAMAI_ORIGIN_HOP',
'HTTP_CF_CONNECTING_IP',
'HTTP_CLIENT_IP',
'HTTP_FASTLY_CLIENT_IP',
'HTTP_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_INCAP_CLIENT_IP',
'HTTP_TRUE_CLIENT_IP',
'HTTP_X_CLIENTIP',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_X_FORWARDED',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_IP_TRAIL',
'HTTP_X_REAL_IP',
'HTTP_X_VARNISH',
'REMOTE_ADDR',
) as $header ) {
if ( isset( $_SERVER[ $header ] ) ) {
$debug_info[ $header ] = array(
'label' => 'Server Variable ' . $header,
'value' => ( $_SERVER[ $header ] ) ? $_SERVER[ $header ] : 'false',
'private' => false,
);
}
}
$debug_info['protect_header'] = array(
'label' => 'Trusted IP',
'value' => wp_json_encode( get_site_option( 'trusted_ip_header' ) ),
'private' => false,
);
/** Sync Debug Information */
$sync_module = Modules::get_module( 'full-sync' );
if ( $sync_module ) {
$sync_statuses = $sync_module->get_status();
$human_readable_sync_status = array();
foreach ( $sync_statuses as $sync_status => $sync_status_value ) {
$human_readable_sync_status[ $sync_status ] =
in_array( $sync_status, array( 'started', 'queue_finished', 'send_started', 'finished' ), true )
? date( 'r', $sync_status_value ) : $sync_status_value;
}
$debug_info['full_sync'] = array(
'label' => 'Full Sync Status',
'value' => wp_json_encode( $human_readable_sync_status ),
'private' => false,
);
}
$queue = Sender::get_instance()->get_sync_queue();
$debug_info['sync_size'] = array(
'label' => 'Sync Queue Size',
'value' => $queue->size(),
'private' => false,
);
$debug_info['sync_lag'] = array(
'label' => 'Sync Queue Lag',
'value' => self::seconds_to_time( $queue->lag() ),
'private' => false,
);
$full_sync_queue = Sender::get_instance()->get_full_sync_queue();
$debug_info['full_sync_size'] = array(
'label' => 'Full Sync Queue Size',
'value' => $full_sync_queue->size(),
'private' => false,
);
$debug_info['full_sync_lag'] = array(
'label' => 'Full Sync Queue Lag',
'value' => self::seconds_to_time( $full_sync_queue->lag() ),
'private' => false,
);
/**
* IDC Information
*
* Must follow sync debug since it depends on sync functionality.
*/
$idc_urls = array(
'home' => Functions::home_url(),
'siteurl' => Functions::site_url(),
'WP_HOME' => Constants::is_defined( 'WP_HOME' ) ? Constants::get_constant( 'WP_HOME' ) : '',
'WP_SITEURL' => Constants::is_defined( 'WP_SITEURL' ) ? Constants::get_constant( 'WP_SITEURL' ) : '',
);
$debug_info['idc_urls'] = array(
'label' => 'IDC URLs',
'value' => wp_json_encode( $idc_urls ),
'private' => false,
);
$debug_info['idc_error_option'] = array(
'label' => 'IDC Error Option',
'value' => wp_json_encode( Jetpack_Options::get_option( 'sync_error_idc' ) ),
'private' => false,
);
$debug_info['idc_optin'] = array(
'label' => 'IDC Opt-in',
'value' => Jetpack::sync_idc_optin(),
'private' => false,
);
// @todo -- Add testing results?
$cxn_tests = new Jetpack_Cxn_Tests();
$debug_info['cxn_tests'] = array(
'label' => 'Connection Tests',
'value' => '',
'private' => false,
);
if ( $cxn_tests->pass() ) {
$debug_info['cxn_tests']['value'] = 'All Pass.';
} else {
$debug_info['cxn_tests']['value'] = wp_json_encode( $cxn_tests->list_fails() );
}
return $debug_info;
}
/**
* Returns a human readable string for which user is the master user.
*
* @return string
*/
private static function human_readable_master_user() {
$master_user = Jetpack_Options::get_option( 'master_user' );
if ( ! $master_user ) {
return __( 'No master user set.', 'jetpack' );
}
$user = new WP_User( $master_user );
if ( ! $user ) {
return __( 'Master user no longer exists. Please disconnect and reconnect Jetpack.', 'jetpack' );
}
return self::human_readable_user( $user );
}
/**
* Return human readable string for a given user object.
*
* @param WP_User|int $user Object or ID.
*
* @return string
*/
private static function human_readable_user( $user ) {
$user = new WP_User( $user );
return sprintf( '#%1$d %2$s (%3$s)', $user->ID, $user->user_login, $user->user_email ); // Format: "#1 username (user@example.com)".
}
}

View File

@@ -0,0 +1,444 @@
<?php
/**
* Jetpack Debugger functionality allowing for self-service diagnostic information via the legacy jetpack debugger.
*
* @package jetpack
*/
/** Ensure the Jetpack_Debug_Data class is available. It should be via the library loaded, but defense is good. */
require_once 'class-jetpack-debug-data.php';
/**
* Class Jetpack_Debugger
*
* A namespacing class for functionality related to the legacy in-plugin diagnostic tooling.
*/
class Jetpack_Debugger {
/**
* Determine the active plan and normalize it for the debugger results.
*
* @return string The plan slug prepended with "JetpackPlan"
*/
private static function what_jetpack_plan() {
// Specifically not deprecating this function since it modifies the output of the Jetpack_Debug_Data::what_jetpack_plan return.
return 'JetpackPlan' . Jetpack_Debug_Data::what_jetpack_plan();
}
/**
* Convert seconds to human readable time.
*
* A dedication function instead of using Core functionality to allow for output in seconds.
*
* @deprecated 7.3.0
*
* @param int $seconds Number of seconds to convert to human time.
*
* @return string Human readable time.
*/
public static function seconds_to_time( $seconds ) {
_deprecated_function( 'Jetpack_Debugger::seconds_to_time', 'Jetpack 7.3.0', 'Jeptack_Debug_Data::seconds_to_time' );
return Jetpack_Debug_Data::seconds_to_time( $seconds );
}
/**
* Returns 30 for use with a filter.
*
* To allow time for WP.com to run upstream testing, this function exists to increase the http_request_timeout value
* to 30.
*
* @return int 30
*/
public static function jetpack_increase_timeout() {
return 30; // seconds.
}
/**
* Disconnect Jetpack and redirect user to connection flow.
*/
public static function disconnect_and_redirect() {
if ( ! ( isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], 'jp_disconnect' ) ) ) {
return;
}
if ( isset( $_GET['disconnect'] ) && $_GET['disconnect'] ) {
if ( Jetpack::is_active() ) {
Jetpack::disconnect();
wp_safe_redirect( Jetpack::admin_url() );
exit;
}
}
}
/**
* Handles output to the browser for the in-plugin debugger.
*/
public static function jetpack_debug_display_handler() {
global $wp_version;
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'jetpack' ) );
}
$support_url = Jetpack::is_development_version()
? 'https://jetpack.com/contact-support/beta-group/'
: 'https://jetpack.com/contact-support/';
$data = Jetpack_Debug_Data::debug_data();
$debug_info = '';
foreach ( $data as $datum ) {
$debug_info .= $datum['label'] . ': ' . $datum['value'] . "\r\n";
}
$debug_info .= "\r\n" . esc_html( 'PHP_VERSION: ' . PHP_VERSION );
$debug_info .= "\r\n" . esc_html( 'WORDPRESS_VERSION: ' . $GLOBALS['wp_version'] );
$debug_info .= "\r\n" . esc_html( 'SITE_URL: ' . site_url() );
$debug_info .= "\r\n" . esc_html( 'HOME_URL: ' . home_url() );
$debug_info .= "\r\n\r\nTEST RESULTS:\r\n\r\n";
$cxntests = new Jetpack_Cxn_Tests();
?>
<div class="wrap">
<h2><?php esc_html_e( 'Debugging Center', 'jetpack' ); ?></h2>
<h3><?php esc_html_e( "Testing your site's compatibility with Jetpack...", 'jetpack' ); ?></h3>
<div class="jetpack-debug-test-container">
<?php
if ( $cxntests->pass() ) {
echo '<div class="jetpack-tests-succeed">' . esc_html__( 'Your Jetpack setup looks a-okay!', 'jetpack' ) . '</div>';
$debug_info .= "All tests passed.\r\n";
$debug_info .= print_r( $cxntests->raw_results(), true ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
} else {
$failures = $cxntests->list_fails();
foreach ( $failures as $fail ) {
echo '<div class="jetpack-test-error">';
echo '<p><a class="jetpack-test-heading" href="#">' . esc_html( $fail['message'] );
echo '<span class="noticon noticon-collapse"></span></a></p>';
echo '<p class="jetpack-test-details">' . esc_html( $fail['resolution'] ) . '</p>';
echo '</div>';
$debug_info .= "FAILED TESTS!\r\n";
$debug_info .= $fail['name'] . ': ' . $fail['message'] . "\r\n";
$debug_info .= print_r( $cxntests->raw_results(), true ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
}
}
?>
</div>
<div class="entry-content">
<h3><?php esc_html_e( 'Trouble with Jetpack?', 'jetpack' ); ?></h3>
<h4><?php esc_html_e( 'It may be caused by one of these issues, which you can diagnose yourself:', 'jetpack' ); ?></h4>
<ol>
<li><b><em>
<?php
esc_html_e( 'A known issue.', 'jetpack' );
?>
</em></b>
<?php
echo sprintf(
wp_kses(
/* translators: URLs to Jetpack support pages. */
__( 'Some themes and plugins have <a href="%1$s" target="_blank">known conflicts</a> with Jetpack check the <a href="%2$s" target="_blank">list</a>. (You can also browse the <a href="%3$s" target="_blank">Jetpack support pages</a> or <a href="%4$s" target="_blank">Jetpack support forum</a> to see if others have experienced and solved the problem.)', 'jetpack' ),
array(
'a' => array(
'href' => array(),
'target' => array(),
),
)
),
'http://jetpack.com/support/getting-started-with-jetpack/known-issues/',
'http://jetpack.com/support/getting-started-with-jetpack/known-issues/',
'http://jetpack.com/support/',
'https://wordpress.org/support/plugin/jetpack'
);
?>
</li>
<li><b><em><?php esc_html_e( 'An incompatible plugin.', 'jetpack' ); ?></em></b> <?php esc_html_e( "Find out by disabling all plugins except Jetpack. If the problem persists, it's not a plugin issue. If the problem is solved, turn your plugins on one by one until the problem pops up again there's the culprit! Let us know, and we'll try to help.", 'jetpack' ); ?></li>
<li>
<b><em><?php esc_html_e( 'A theme conflict.', 'jetpack' ); ?></em></b>
<?php
$default_theme = wp_get_theme( WP_DEFAULT_THEME );
if ( $default_theme->exists() ) {
/* translators: %s is the name of a theme */
echo esc_html( sprintf( __( "If your problem isn't known or caused by a plugin, try activating %s (the default WordPress theme).", 'jetpack' ), $default_theme->get( 'Name' ) ) );
} else {
esc_html_e( "If your problem isn't known or caused by a plugin, try activating the default WordPress theme.", 'jetpack' );
}
?>
<?php esc_html_e( "If this solves the problem, something in your theme is probably broken let the theme's author know.", 'jetpack' ); ?>
</li>
<li><b><em><?php esc_html_e( 'A problem with your XMLRPC file.', 'jetpack' ); ?></em></b>
<?php
echo sprintf(
wp_kses(
/* translators: The URL to the site's xmlrpc.php file. */
__( 'Load your <a href="%s">XMLRPC file</a>. It should say “XML-RPC server accepts POST requests only.” on a line by itself.', 'jetpack' ),
array( 'a' => array( 'href' => array() ) )
),
esc_attr( site_url( 'xmlrpc.php' ) )
);
?>
<ul>
<li>- <?php esc_html_e( "If it's not by itself, a theme or plugin is displaying extra characters. Try steps 2 and 3.", 'jetpack' ); ?></li>
<li>- <?php esc_html_e( 'If you get a 404 message, contact your web host. Their security may block XMLRPC.', 'jetpack' ); ?></li>
</ul>
</li>
<?php if ( current_user_can( 'jetpack_disconnect' ) && Jetpack::is_active() ) : ?>
<li>
<strong><em><?php esc_html_e( 'A connection problem with WordPress.com.', 'jetpack' ); ?></em></strong>
<?php
echo sprintf(
wp_kses(
/* translators: URL to disconnect and reconnect Jetpack. */
__( 'Jetpack works by connecting to WordPress.com for a lot of features. Sometimes, when the connection gets messed up, you need to disconnect and reconnect to get things working properly. <a href="%s">Disconnect from WordPress.com</a>', 'jetpack' ),
array(
'a' => array(
'href' => array(),
'class' => array(),
),
)
),
esc_attr(
wp_nonce_url(
Jetpack::admin_url(
array(
'page' => 'jetpack-debugger',
'disconnect' => true,
)
),
'jp_disconnect',
'nonce'
)
)
);
?>
</li>
<?php endif; ?>
</ol>
<h4><?php esc_html_e( 'Still having trouble?', 'jetpack' ); ?></h4>
<p><b><em><?php esc_html_e( 'Ask us for help!', 'jetpack' ); ?></em></b>
<?php
/**
* Offload to new WordPress debug data in WP 5.2+
*
* @todo remove fallback when 5.2 is the minimum supported.
*/
if ( version_compare( $wp_version, '5.2-alpha', '>=' ) ) {
echo sprintf(
wp_kses(
/* translators: URL for Jetpack support. URL for WordPress's Site Health */
__( '<a href="%1$s">Contact our Happiness team</a>. When you do, please include the <a href="%2$s">full debug information from your site</a>.', 'jetpack' ),
array( 'a' => array( 'href' => array() ) )
),
esc_url( $support_url ),
esc_url( admin_url() . 'site-health.php?tab=debug' )
);
$hide_debug = true;
} else { // Versions before 5.2, fallback.
echo sprintf(
wp_kses(
/* translators: URL for Jetpack support. */
__( '<a href="%s">Contact our Happiness team</a>. When you do, please include the full debug information below.', 'jetpack' ),
array( 'a' => array( 'href' => array() ) )
),
esc_url( $support_url )
);
$hide_debug = false;
}
?>
</p>
<hr />
<?php if ( Jetpack::is_active() ) : ?>
<div id="connected-user-details">
<h3><?php esc_html_e( 'More details about your Jetpack settings', 'jetpack' ); ?></h3>
<p>
<?php
printf(
wp_kses(
/* translators: %s is an e-mail address */
__( 'The primary connection is owned by <strong>%s</strong>\'s WordPress.com account.', 'jetpack' ),
array( 'strong' => array() )
),
esc_html( Jetpack::get_master_user_email() )
);
?>
</p>
</div>
<?php else : ?>
<div id="dev-mode-details">
<p>
<?php
printf(
wp_kses(
/* translators: Link to a Jetpack support page. */
__( 'Would you like to use Jetpack on your local development site? You can do so thanks to <a href="%s">Jetpack\'s development mode</a>.', 'jetpack' ),
array( 'a' => array( 'href' => array() ) )
),
'https://jetpack.com/support/development-mode/'
);
?>
</p>
</div>
<?php endif; ?>
<?php
if (
current_user_can( 'jetpack_manage_modules' )
&& ( Jetpack::is_development_mode() || Jetpack::is_active() )
) {
printf(
wp_kses(
'<p><a href="%1$s">%2$s</a></p>',
array(
'a' => array( 'href' => array() ),
'p' => array(),
)
),
esc_attr( Jetpack::admin_url( 'page=jetpack_modules' ) ),
esc_html__( 'Access the full list of Jetpack modules available on your site.', 'jetpack' )
);
}
?>
</div>
<hr />
<?php
if ( ! $hide_debug ) {
?>
<div id="toggle_debug_info"><?php esc_html_e( 'Advanced Debug Results', 'jetpack' ); ?></div>
<div id="debug_info_div">
<h4><?php esc_html_e( 'Debug Info', 'jetpack' ); ?></h4>
<div id="debug_info"><pre><?php echo esc_html( $debug_info ); ?></pre></div>
</div>
<?php
}
?>
</div>
<?php
}
/**
* Outputs html needed within the <head> for the in-plugin debugger page.
*/
public static function jetpack_debug_admin_head() {
Jetpack_Admin_Page::load_wrapper_styles();
?>
<style type="text/css">
.jetpack-debug-test-container {
margin-top: 20px;
margin-bottom: 30px;
}
.jetpack-tests-succeed {
font-size: large;
color: #8BAB3E;
}
.jetpack-test-details {
margin: 4px 6px;
padding: 10px;
overflow: auto;
display: none;
}
.jetpack-test-error {
margin-bottom: 10px;
background: #FFEBE8;
border: solid 1px #C00;
border-radius: 3px;
}
.jetpack-test-error p {
margin: 0;
padding: 0;
}
p.jetpack-test-details {
margin: 4px 6px;
padding: 10px;
}
.jetpack-test-error a.jetpack-test-heading {
padding: 4px 6px;
display: block;
text-decoration: none;
color: inherit;
}
.jetpack-test-error .noticon {
float: right;
}
.formbox {
margin: 0 0 25px 0;
}
.formbox input[type="text"], .formbox input[type="email"], .formbox input[type="url"], .formbox textarea, #debug_info_div {
border: 1px solid #e5e5e5;
border-radius: 11px;
box-shadow: inset 0 1px 1px rgba(0,0,0,0.1);
color: #666;
font-size: 14px;
padding: 10px;
width: 97%;
}
#debug_info_div {
border-radius: 0;
margin-top: 16px;
background: #FFF;
padding: 16px;
}
.formbox .contact-support input[type="submit"] {
float: right;
margin: 0 !important;
border-radius: 20px !important;
cursor: pointer;
font-size: 13pt !important;
height: auto !important;
margin: 0 0 2em 10px !important;
padding: 8px 16px !important;
background-color: #ddd;
border: 1px solid rgba(0,0,0,0.05);
border-top-color: rgba(255,255,255,0.1);
border-bottom-color: rgba(0,0,0,0.15);
color: #333;
font-weight: 400;
display: inline-block;
text-align: center;
text-decoration: none;
}
.formbox span.errormsg {
margin: 0 0 10px 10px;
color: #d00;
display: none;
}
.formbox.error span.errormsg {
display: block;
}
#debug_info_div, #toggle_debug_info, #debug_info_div p {
font-size: 12px;
}
#category_div ul li {
list-style-type: none;
}
</style>
<script type="text/javascript">
jQuery( document ).ready( function($) {
$( '#debug_info' ).prepend( 'jQuery version: ' + jQuery.fn.jquery + "\r\n" );
$( '#debug_form_info' ).prepend( 'jQuery version: ' + jQuery.fn.jquery + "\r\n" );
$( '.jetpack-test-error .jetpack-test-heading' ).on( 'click', function() {
$( this ).parents( '.jetpack-test-error' ).find( '.jetpack-test-details' ).slideToggle();
return false;
} );
} );
</script>
<?php
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* WP Site Health functionality temporarily stored in this file until all of Jetpack is PHP 5.3+
*
* @package Jetpack.
*/
/**
* Test runner for Core's Site Health module.
*
* @since 7.3.0
*/
function jetpack_debugger_ajax_local_testing_suite() {
check_ajax_referer( 'health-check-site-status' );
if ( ! current_user_can( 'jetpack_manage_modules' ) ) {
wp_send_json_error();
}
$tests = new Jetpack_Cxn_Tests();
wp_send_json_success( $tests->output_results_for_core_async_site_health() );
}
/**
* Adds the Jetpack Local Testing Suite to the Core Site Health system.
*
* @since 7.3.0
*
* @param array $core_tests Array of tests from Core's Site Health.
*
* @return array $core_tests Array of tests for Core's Site Health.
*/
function jetpack_debugger_site_status_tests( $core_tests ) {
$cxn_tests = new Jetpack_Cxn_Tests();
$tests = $cxn_tests->list_tests( 'direct' );
foreach ( $tests as $test ) {
$core_tests['direct'][ $test['name'] ] = array(
'label' => __( 'Jetpack: ', 'jetpack' ) . $test['name'],
'test' => function() use ( $test, $cxn_tests ) { // phpcs:ignore PHPCompatibility.FunctionDeclarations.NewClosure.Found
$results = $cxn_tests->run_test( $test['name'] );
// Test names are, by default, `test__some_string_of_text`. Let's convert to "Some String Of Text" for humans.
$label = ucwords(
str_replace(
'_',
' ',
str_replace( 'test__', '', $test['name'] )
)
);
$return = array(
'label' => $label,
'status' => 'good',
'badge' => array(
'label' => __( 'Jetpack', 'jetpack' ),
'color' => 'green',
),
'description' => sprintf(
'<p>%s</p>',
__( 'This test successfully passed!', 'jetpack' )
),
'actions' => '',
'test' => 'jetpack_' . $test['name'],
);
if ( is_wp_error( $results ) ) {
return;
}
if ( false === $results['pass'] ) {
$return['label'] = $results['message'];
$return['status'] = $results['severity'];
$return['description'] = sprintf(
'<p>%s</p>',
$results['resolution']
);
if ( ! empty( $results['action'] ) ) {
$return['actions'] = sprintf(
'<a class="button button-primary" href="%1$s" target="_blank" rel="noopener noreferrer">%2$s <span class="screen-reader-text">%3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>',
esc_url( $results['action'] ),
__( 'Resolve', 'jetpack' ),
/* translators: accessibility text */
__( '(opens in a new tab)', 'jetpack' )
);
}
}
return $return;
},
);
}
$core_tests['async']['jetpack_test_suite'] = array(
'label' => __( 'Jetpack Tests', 'jetpack' ),
'test' => 'jetpack_local_testing_suite',
);
return $core_tests;
}