2019-09-27 10:12:59 +02:00
< ? php
namespace Activitypub ;
2023-09-27 11:14:52 +02:00
use WP_Error ;
use Activitypub\Webfinger ;
use function Activitypub\get_plugin_version ;
use function Activitypub\get_webfinger_resource ;
2019-09-27 10:12:59 +02:00
/**
* ActivityPub Health_Check Class
*
* @ author Matthias Pfefferle
*/
class Health_Check {
2021-07-23 15:46:28 +02:00
/**
* Initialize health checks
*
* @ return void
*/
2019-09-27 10:12:59 +02:00
public static function init () {
2023-04-20 15:22:11 +02:00
\add_filter ( 'site_status_tests' , array ( self :: class , 'add_tests' ) );
\add_filter ( 'debug_information' , array ( self :: class , 'debug_information' ) );
2020-07-21 09:23:35 +02:00
}
public static function add_tests ( $tests ) {
2021-07-23 15:46:28 +02:00
$tests [ 'direct' ][ 'activitypub_test_author_url' ] = array (
'label' => \__ ( 'Author URL test' , 'activitypub' ),
2023-04-20 15:22:11 +02:00
'test' => array ( self :: class , 'test_author_url' ),
2020-07-21 09:23:35 +02:00
);
2021-07-23 15:46:28 +02:00
$tests [ 'direct' ][ 'activitypub_test_webfinger' ] = array (
'label' => __ ( 'WebFinger Test' , 'activitypub' ),
2023-04-20 15:22:11 +02:00
'test' => array ( self :: class , 'test_webfinger' ),
2021-07-23 15:46:28 +02:00
);
2020-07-21 09:23:35 +02:00
2023-09-27 11:14:52 +02:00
$tests [ 'direct' ][ 'activitypub_test_system_cron' ] = array (
'label' => __ ( 'System Cron Test' , 'activitypub' ),
'test' => array ( self :: class , 'test_system_cron' ),
);
2020-07-21 09:23:35 +02:00
return $tests ;
}
2021-07-23 15:46:28 +02:00
/**
* Author URL tests
*
2023-03-23 08:35:26 +01:00
* @ return array
2021-07-23 15:46:28 +02:00
*/
public static function test_author_url () {
2020-07-21 09:23:35 +02:00
$result = array (
2021-01-13 23:22:17 +01:00
'label' => \__ ( 'Author URL accessible' , 'activitypub' ),
2020-07-21 09:23:35 +02:00
'status' => 'good' ,
'badge' => array (
'label' => \__ ( 'ActivityPub' , 'activitypub' ),
'color' => 'green' ,
),
'description' => \sprintf (
'<p>%s</p>' ,
2021-01-13 23:22:17 +01:00
\__ ( 'Your author URL is accessible and supports the required "Accept" header.' , 'activitypub' )
2020-07-21 09:23:35 +02:00
),
'actions' => '' ,
2021-07-23 15:46:28 +02:00
'test' => 'test_author_url' ,
);
$check = self :: is_author_url_accessible ();
if ( true === $check ) {
return $result ;
}
$result [ 'status' ] = 'critical' ;
$result [ 'label' ] = \__ ( 'Author URL is not accessible' , 'activitypub' );
$result [ 'badge' ][ 'color' ] = 'red' ;
$result [ 'description' ] = \sprintf (
'<p>%s</p>' ,
$check -> get_error_message ()
2020-07-21 09:23:35 +02:00
);
2021-07-23 15:46:28 +02:00
return $result ;
}
2020-07-21 09:23:35 +02:00
2023-09-27 11:14:52 +02:00
/**
* System Cron tests
*
* @ return array
*/
public static function test_system_cron () {
$result = array (
'label' => \__ ( 'System Task Scheduler configured' , 'activitypub' ),
'status' => 'good' ,
'badge' => array (
'label' => \__ ( 'ActivityPub' , 'activitypub' ),
'color' => 'green' ,
),
'description' => \sprintf (
'<p>%s</p>' ,
\esc_html__ ( 'You seem to use the System Task Scheduler to process WP_Cron tasks.' , 'activitypub' )
),
'actions' => '' ,
'test' => 'test_system_cron' ,
);
if ( defined ( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
return $result ;
}
$result [ 'status' ] = 'recommended' ;
$result [ 'label' ] = \__ ( 'System Task Scheduler not configured' , 'activitypub' );
$result [ 'badge' ][ 'color' ] = 'orange' ;
$result [ 'description' ] = \sprintf (
'<p>%s</p>' ,
\__ ( 'It is highly recommended to use your Systems Task Scheduler instead of the default <code>WP_Cron</code> setup. For further informations, check the "<a href="https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/" target="_blank">Hooking WP-Cron Into the System Task Scheduler</a>" from the Plugin Handbook.' , 'activitypub' )
);
return $result ;
}
2021-07-23 15:46:28 +02:00
/**
* WebFinger tests
*
2023-03-23 08:35:26 +01:00
* @ return array
2021-07-23 15:46:28 +02:00
*/
public static function test_webfinger () {
$result = array (
'label' => \__ ( 'WebFinger endpoint' , 'activitypub' ),
'status' => 'good' ,
'badge' => array (
'label' => \__ ( 'ActivityPub' , 'activitypub' ),
'color' => 'green' ,
),
'description' => \sprintf (
2020-07-21 09:23:35 +02:00
'<p>%s</p>' ,
2023-03-23 08:35:26 +01:00
\__ ( 'Your WebFinger endpoint is accessible and returns the correct information.' , 'activitypub' )
2021-07-23 15:46:28 +02:00
),
'actions' => '' ,
'test' => 'test_webfinger' ,
);
$check = self :: is_webfinger_endpoint_accessible ();
if ( true === $check ) {
return $result ;
2020-07-21 09:23:35 +02:00
}
2021-07-23 15:46:28 +02:00
$result [ 'status' ] = 'critical' ;
$result [ 'label' ] = \__ ( 'WebFinger endpoint is not accessible' , 'activitypub' );
$result [ 'badge' ][ 'color' ] = 'red' ;
$result [ 'description' ] = \sprintf (
'<p>%s</p>' ,
$check -> get_error_message ()
);
2020-07-21 09:23:35 +02:00
return $result ;
}
2021-07-23 15:46:28 +02:00
/**
2023-03-23 08:35:26 +01:00
* Check if `author_posts_url` is accessible and that request returns correct JSON
2021-07-23 15:46:28 +02:00
*
2023-09-27 11:14:52 +02:00
* @ return boolean | WP_Error
2021-07-23 15:46:28 +02:00
*/
public static function is_author_url_accessible () {
2020-07-21 09:23:35 +02:00
$user = \wp_get_current_user ();
$author_url = \get_author_posts_url ( $user -> ID );
2021-01-13 23:22:17 +01:00
$reference_author_url = self :: get_author_posts_url ( $user -> ID , $user -> user_nicename );
2020-07-21 09:23:35 +02:00
// check for "author" in URL
2021-01-13 23:22:17 +01:00
if ( $author_url !== $reference_author_url ) {
2023-09-27 11:14:52 +02:00
return new WP_Error (
2021-07-23 15:46:28 +02:00
'author_url_not_accessible' ,
\sprintf (
// translators: %s: Author URL
\__ (
2023-07-18 22:02:27 +02:00
'Your author URL <code>%s</code> was replaced, this is often done by plugins.' ,
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
2020-07-21 09:23:35 +02:00
}
// try to access author URL
2021-07-23 15:46:28 +02:00
$response = \wp_remote_get (
$author_url ,
array (
'headers' => array ( 'Accept' => 'application/activity+json' ),
'redirection' => 0 ,
)
);
2020-07-21 09:23:35 +02:00
if ( \is_wp_error ( $response ) ) {
2023-09-27 11:14:52 +02:00
return new WP_Error (
2021-07-23 15:46:28 +02:00
'author_url_not_accessible' ,
\sprintf (
// translators: %s: Author URL
\__ (
2023-07-18 22:02:27 +02:00
'Your author URL <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure. If the setup seems fine, maybe check if a plugin might restrict the access.' ,
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
}
$response_code = \wp_remote_retrieve_response_code ( $response );
// check for redirects
if ( \in_array ( $response_code , array ( 301 , 302 , 307 , 308 ), true ) ) {
2023-09-27 11:14:52 +02:00
return new WP_Error (
2021-07-23 15:46:28 +02:00
'author_url_not_accessible' ,
\sprintf (
// translators: %s: Author URL
\__ (
2023-07-18 22:02:27 +02:00
'Your author URL <code>%s</code> is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".' ,
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
2020-07-21 09:23:35 +02:00
}
// check if response is JSON
$body = \wp_remote_retrieve_body ( $response );
if ( ! \is_string ( $body ) || ! \is_array ( \json_decode ( $body , true ) ) ) {
2023-09-27 11:14:52 +02:00
return new WP_Error (
2021-07-23 15:46:28 +02:00
'author_url_not_accessible' ,
\sprintf (
// translators: %s: Author URL
\__ (
2023-07-18 22:02:27 +02:00
'Your author URL <code>%s</code> does not return valid JSON for <code>application/activity+json</code>. Please check if your hosting supports alternate <code>Accept</code> headers.' ,
2021-07-23 15:46:28 +02:00
'activitypub'
),
$author_url
)
);
}
return true ;
}
/**
2023-03-23 08:35:26 +01:00
* Check if WebFinger endpoint is accessible and profile request returns correct JSON
2021-07-23 15:46:28 +02:00
*
2023-09-27 11:14:52 +02:00
* @ return boolean | WP_Error
2021-07-23 15:46:28 +02:00
*/
public static function is_webfinger_endpoint_accessible () {
2022-11-09 15:08:32 +01:00
$user = \wp_get_current_user ();
2023-09-27 11:14:52 +02:00
$account = get_webfinger_resource ( $user -> ID );
2021-07-23 15:46:28 +02:00
2023-09-27 11:14:52 +02:00
$url = Webfinger :: resolve ( $account );
2022-11-09 15:08:32 +01:00
if ( \is_wp_error ( $url ) ) {
2023-07-18 22:13:53 +02:00
$allowed = array ( 'code' => array () );
$not_accessible = wp_kses (
// translators: %s: Author URL
\__ (
'Your WebFinger endpoint <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure.' ,
'activitypub'
),
$allowed
);
$invalid_response = wp_kses (
// translators: %s: Author URL
\__ (
'Your WebFinger endpoint <code>%s</code> does not return valid JSON for <code>application/jrd+json</code>.' ,
'activitypub'
),
$allowed
);
2022-11-09 15:08:32 +01:00
$health_messages = array (
'webfinger_url_not_accessible' => \sprintf (
2023-07-18 22:13:53 +02:00
$not_accessible ,
2022-11-09 15:08:32 +01:00
$url -> get_error_data ()
),
'webfinger_url_invalid_response' => \sprintf (
2021-07-23 15:46:28 +02:00
// translators: %s: Author URL
2023-07-18 22:13:53 +02:00
$invalid_response ,
2022-11-09 15:08:32 +01:00
$url -> get_error_data ()
),
);
$message = null ;
2022-11-09 15:27:50 +01:00
if ( isset ( $health_messages [ $url -> get_error_code () ] ) ) {
2022-11-09 15:08:32 +01:00
$message = $health_messages [ $url -> get_error_code () ];
}
2023-09-27 11:14:52 +02:00
return new WP_Error (
2022-11-09 15:08:32 +01:00
$url -> get_error_code (),
$message ,
$url -> get_error_data ()
2021-07-23 15:46:28 +02:00
);
2020-07-21 09:23:35 +02:00
}
return true ;
2019-09-27 10:12:59 +02:00
}
2021-01-13 23:22:17 +01:00
/**
* Retrieve the URL to the author page for the user with the ID provided .
*
* @ global WP_Rewrite $wp_rewrite WordPress rewrite component .
*
* @ param int $author_id Author ID .
* @ param string $author_nicename Optional . The author ' s nicename ( slug ) . Default empty .
*
* @ return string The URL to the author ' s page .
*/
public static function get_author_posts_url ( $author_id , $author_nicename = '' ) {
global $wp_rewrite ;
$auth_id = ( int ) $author_id ;
$link = $wp_rewrite -> get_author_permastruct ();
if ( empty ( $link ) ) {
$file = home_url ( '/' );
$link = $file . '?author=' . $auth_id ;
} else {
if ( '' === $author_nicename ) {
$user = get_userdata ( $author_id );
if ( ! empty ( $user -> user_nicename ) ) {
$author_nicename = $user -> user_nicename ;
}
}
$link = str_replace ( '%author%' , $author_nicename , $link );
$link = home_url ( user_trailingslashit ( $link ) );
}
return $link ;
}
2022-11-22 00:05:17 +01:00
/**
* Static function for generating site debug data when required .
*
* @ param array $info The debug information to be added to the core information page .
2023-03-23 08:35:26 +01:00
* @ return array The filtered information
2022-11-22 00:05:17 +01:00
*/
public static function debug_information ( $info ) {
$info [ 'activitypub' ] = array (
2022-12-06 17:38:32 +01:00
'label' => __ ( 'ActivityPub' , 'activitypub' ),
2022-11-22 00:05:17 +01:00
'fields' => array (
'webfinger' => array (
2022-12-06 17:20:01 +01:00
'label' => __ ( 'WebFinger Resource' , 'activitypub' ),
2023-09-27 11:14:52 +02:00
'value' => Webfinger :: get_user_resource ( wp_get_current_user () -> ID ),
2022-11-22 00:05:17 +01:00
'private' => true ,
),
'author_url' => array (
'label' => __ ( 'Author URL' , 'activitypub' ),
'value' => get_author_posts_url ( wp_get_current_user () -> ID ),
'private' => true ,
),
2023-09-27 11:14:52 +02:00
'plugin_version' => array (
'label' => __ ( 'Plugin Version' , 'activitypub' ),
'value' => get_plugin_version (),
'private' => true ,
),
2022-11-22 00:05:17 +01:00
),
);
return $info ;
}
2019-09-27 10:12:59 +02:00
}