2020-04-07 13:03:04 +00:00
< ? php
2024-10-09 12:44:17 +00:00
/**
* Health_Check class .
*
* @ package Activitypub
*/
2020-04-07 13:03:04 +00:00
namespace Activitypub ;
2023-10-22 22:20:53 +00:00
use WP_Error ;
use Activitypub\Collection\Users ;
2020-04-07 13:03:04 +00:00
/**
2024-10-09 12:44:17 +00:00
* ActivityPub Health_Check Class .
2020-04-07 13:03:04 +00:00
*
* @ author Matthias Pfefferle
*/
class Health_Check {
2021-07-25 23:24:56 +00:00
/**
2024-10-09 12:44:17 +00:00
* Initialize health checks .
2021-07-25 23:24:56 +00:00
*/
2020-04-07 13:03:04 +00:00
public static function init () {
2023-10-22 22:20:53 +00:00
\add_filter ( 'site_status_tests' , array ( self :: class , 'add_tests' ) );
\add_filter ( 'debug_information' , array ( self :: class , 'debug_information' ) );
2020-12-25 19:23:08 +00:00
}
2024-10-09 12:44:17 +00:00
/**
* Add tests to the Site Health Check .
*
* @ param array $tests The test array .
*
* @ return array The filtered test array .
*/
2020-12-25 19:23:08 +00:00
public static function add_tests ( $tests ) {
2024-05-09 15:26:55 +00:00
if ( ! is_user_disabled ( get_current_user_id () ) ) {
2023-10-22 22:20:53 +00:00
$tests [ 'direct' ][ 'activitypub_test_author_url' ] = array (
'label' => \__ ( 'Author URL test' , 'activitypub' ),
'test' => array ( self :: class , 'test_author_url' ),
);
}
2020-12-25 19:23:08 +00:00
2021-07-25 23:24:56 +00:00
$tests [ 'direct' ][ 'activitypub_test_webfinger' ] = array (
'label' => __ ( 'WebFinger Test' , 'activitypub' ),
2023-10-22 22:20:53 +00:00
'test' => array ( self :: class , 'test_webfinger' ),
);
2020-12-25 19:23:08 +00:00
return $tests ;
}
2021-07-25 23:24:56 +00:00
/**
2024-10-09 12:44:17 +00:00
* Author URL tests .
2021-07-25 23:24:56 +00:00
*
2023-10-22 22:20:53 +00:00
* @ return array
2021-07-25 23:24:56 +00:00
*/
public static function test_author_url () {
2020-12-25 19:23:08 +00:00
$result = array (
2021-07-25 23:24:56 +00:00
'label' => \__ ( 'Author URL accessible' , 'activitypub' ),
2020-12-25 19:23:08 +00:00
'status' => 'good' ,
'badge' => array (
'label' => \__ ( 'ActivityPub' , 'activitypub' ),
'color' => 'green' ,
),
'description' => \sprintf (
'<p>%s</p>' ,
2021-07-25 23:24:56 +00:00
\__ ( 'Your author URL is accessible and supports the required "Accept" header.' , 'activitypub' )
2020-12-25 19:23:08 +00:00
),
'actions' => '' ,
2021-07-25 23:24:56 +00: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-12-25 19:23:08 +00:00
);
2021-07-25 23:24:56 +00:00
return $result ;
}
2020-12-25 19:23:08 +00:00
2023-10-22 22:20:53 +00:00
/**
2024-10-09 12:44:17 +00:00
* System Cron tests .
2023-10-22 22:20:53 +00:00
*
* @ 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>' ,
\__ ( 'Enhance your WordPress site’ s performance and mitigate potential heavy loads caused by plugins like ActivityPub by setting up a system cron job to run WP Cron. This ensures scheduled tasks are executed consistently and reduces the reliance on website traffic for trigger events.' , 'activitypub' )
);
2024-10-09 12:44:17 +00:00
$result [ 'actions' ] .= sprintf (
2023-10-22 22:20:53 +00:00
'<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>' ,
2024-10-09 12:44:17 +00:00
esc_url ( __ ( 'https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/' , 'activitypub' ) ),
2023-10-22 22:20:53 +00:00
__ ( 'Learn how to hook the WP-Cron into the System Task Scheduler.' , 'activitypub' ),
/* translators: Hidden accessibility text. */
__ ( '(opens in a new tab)' , 'activitypub' )
);
return $result ;
}
2021-07-25 23:24:56 +00:00
/**
2024-10-09 12:44:17 +00:00
* WebFinger tests .
2021-07-25 23:24:56 +00:00
*
2023-10-22 22:20:53 +00:00
* @ return array
2021-07-25 23:24:56 +00:00
*/
public static function test_webfinger () {
$result = array (
'label' => \__ ( 'WebFinger endpoint' , 'activitypub' ),
'status' => 'good' ,
'badge' => array (
'label' => \__ ( 'ActivityPub' , 'activitypub' ),
'color' => 'green' ,
),
'description' => \sprintf (
2020-12-25 19:23:08 +00:00
'<p>%s</p>' ,
2023-10-22 22:20:53 +00:00
\__ ( 'Your WebFinger endpoint is accessible and returns the correct information.' , 'activitypub' )
2021-07-25 23:24:56 +00:00
),
'actions' => '' ,
'test' => 'test_webfinger' ,
);
$check = self :: is_webfinger_endpoint_accessible ();
if ( true === $check ) {
return $result ;
2020-12-25 19:23:08 +00:00
}
2021-07-25 23:24:56 +00: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-12-25 19:23:08 +00:00
return $result ;
}
2021-07-25 23:24:56 +00:00
/**
2024-10-09 12:44:17 +00:00
* Check if `author_posts_url` is accessible and that request returns correct JSON .
2021-07-25 23:24:56 +00:00
*
2024-10-09 12:44:17 +00:00
* @ return bool | WP_Error True if the author URL is accessible , WP_Error otherwise .
2021-07-25 23:24:56 +00:00
*/
public static function is_author_url_accessible () {
2024-10-09 12:44:17 +00:00
$user = \wp_get_current_user ();
$author_url = \get_author_posts_url ( $user -> ID );
2021-07-25 23:24:56 +00:00
$reference_author_url = self :: get_author_posts_url ( $user -> ID , $user -> user_nicename );
2020-12-25 19:23:08 +00:00
2024-10-09 12:44:17 +00:00
// Check for "author" in URL.
2021-07-25 23:24:56 +00:00
if ( $author_url !== $reference_author_url ) {
2023-10-22 22:20:53 +00:00
return new WP_Error (
2021-07-25 23:24:56 +00:00
'author_url_not_accessible' ,
\sprintf (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2021-07-25 23:24:56 +00:00
\__ (
2023-10-22 22:20:53 +00:00
'Your author URL <code>%s</code> was replaced, this is often done by plugins.' ,
2021-07-25 23:24:56 +00:00
'activitypub'
),
$author_url
)
);
2020-12-25 19:23:08 +00:00
}
2024-10-09 12:44:17 +00:00
// Try to access author URL.
2021-07-25 23:24:56 +00:00
$response = \wp_remote_get (
$author_url ,
array (
2024-10-09 12:44:17 +00:00
'headers' => array ( 'Accept' => 'application/activity+json' ),
2021-07-25 23:24:56 +00:00
'redirection' => 0 ,
)
);
2020-12-25 19:23:08 +00:00
if ( \is_wp_error ( $response ) ) {
2023-10-22 22:20:53 +00:00
return new WP_Error (
2021-07-25 23:24:56 +00:00
'author_url_not_accessible' ,
\sprintf (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2021-07-25 23:24:56 +00:00
\__ (
2023-10-22 22:20:53 +00: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-25 23:24:56 +00:00
'activitypub'
),
$author_url
)
);
}
$response_code = \wp_remote_retrieve_response_code ( $response );
2024-10-09 12:44:17 +00:00
// Check for redirects.
2021-07-25 23:24:56 +00:00
if ( \in_array ( $response_code , array ( 301 , 302 , 307 , 308 ), true ) ) {
2023-10-22 22:20:53 +00:00
return new WP_Error (
2021-07-25 23:24:56 +00:00
'author_url_not_accessible' ,
\sprintf (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2021-07-25 23:24:56 +00:00
\__ (
2023-10-22 22:20:53 +00:00
'Your author URL <code>%s</code> is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".' ,
2021-07-25 23:24:56 +00:00
'activitypub'
),
$author_url
)
);
2020-12-25 19:23:08 +00:00
}
2024-10-09 12:44:17 +00:00
// Check if response is JSON.
2020-12-25 19:23:08 +00:00
$body = \wp_remote_retrieve_body ( $response );
if ( ! \is_string ( $body ) || ! \is_array ( \json_decode ( $body , true ) ) ) {
2023-10-22 22:20:53 +00:00
return new WP_Error (
2021-07-25 23:24:56 +00:00
'author_url_not_accessible' ,
\sprintf (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2021-07-25 23:24:56 +00:00
\__ (
2023-10-22 22:20:53 +00: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-25 23:24:56 +00:00
'activitypub'
),
$author_url
)
);
}
return true ;
}
/**
2023-10-22 22:20:53 +00:00
* Check if WebFinger endpoint is accessible and profile request returns correct JSON
2021-07-25 23:24:56 +00:00
*
* @ return boolean | WP_Error
*/
public static function is_webfinger_endpoint_accessible () {
2024-10-09 12:44:17 +00:00
$user = Users :: get_by_id ( Users :: APPLICATION_USER_ID );
2024-02-08 12:31:25 +00:00
$resource = $user -> get_webfinger ();
2021-07-25 23:24:56 +00:00
2024-02-08 12:31:25 +00:00
$url = Webfinger :: resolve ( $resource );
2022-12-19 23:08:11 +00:00
if ( \is_wp_error ( $url ) ) {
2023-10-22 22:20:53 +00:00
$allowed = array ( 'code' => array () );
2024-10-09 12:44:17 +00:00
2023-10-22 22:20:53 +00:00
$not_accessible = wp_kses (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2023-10-22 22:20:53 +00:00
\__ (
'Your WebFinger endpoint <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure.' ,
'activitypub'
),
$allowed
);
$invalid_response = wp_kses (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2023-10-22 22:20:53 +00:00
\__ (
'Your WebFinger endpoint <code>%s</code> does not return valid JSON for <code>application/jrd+json</code>.' ,
'activitypub'
),
$allowed
);
2022-12-19 23:08:11 +00:00
$health_messages = array (
2024-10-09 12:44:17 +00:00
'webfinger_url_not_accessible' => \sprintf (
2023-10-22 22:20:53 +00:00
$not_accessible ,
2024-10-09 12:44:17 +00:00
$url -> get_error_data ()[ 'data' ]
2022-12-19 23:08:11 +00:00
),
'webfinger_url_invalid_response' => \sprintf (
2024-10-09 12:44:17 +00:00
// translators: %s: Author URL.
2023-10-22 22:20:53 +00:00
$invalid_response ,
2024-10-09 12:44:17 +00:00
$url -> get_error_data ()[ 'data' ]
2022-12-19 23:08:11 +00:00
),
);
2024-10-09 12:44:17 +00:00
$message = null ;
2022-12-19 23:08:11 +00:00
if ( isset ( $health_messages [ $url -> get_error_code () ] ) ) {
$message = $health_messages [ $url -> get_error_code () ];
}
2024-10-09 12:44:17 +00:00
2023-10-22 22:20:53 +00:00
return new WP_Error (
2022-12-19 23:08:11 +00:00
$url -> get_error_code (),
$message ,
$url -> get_error_data ()
2021-07-25 23:24:56 +00:00
);
2020-12-25 19:23:08 +00:00
}
return true ;
2020-04-07 13:03:04 +00:00
}
2021-07-25 23:24:56 +00:00
/**
* Retrieve the URL to the author page for the user with the ID provided .
*
2024-10-09 12:44:17 +00:00
* @ global \WP_Rewrite $wp_rewrite WordPress rewrite component .
2021-07-25 23:24:56 +00:00
*
* @ 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 ;
2024-10-09 12:44:17 +00:00
2021-07-25 23:24:56 +00:00
$auth_id = ( int ) $author_id ;
2024-10-09 12:44:17 +00:00
$link = $wp_rewrite -> get_author_permastruct ();
2021-07-25 23:24:56 +00:00
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-12-19 23:08:11 +00: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-10-22 22:20:53 +00:00
* @ return array The filtered information
2022-12-19 23:08:11 +00:00
*/
public static function debug_information ( $info ) {
$info [ 'activitypub' ] = array (
'label' => __ ( 'ActivityPub' , 'activitypub' ),
'fields' => array (
2024-10-09 12:44:17 +00:00
'webfinger' => array (
2022-12-19 23:08:11 +00:00
'label' => __ ( 'WebFinger Resource' , 'activitypub' ),
2023-10-22 22:20:53 +00:00
'value' => Webfinger :: get_user_resource ( wp_get_current_user () -> ID ),
2022-12-19 23:08:11 +00:00
'private' => true ,
),
2024-10-09 12:44:17 +00:00
'author_url' => array (
2022-12-19 23:08:11 +00:00
'label' => __ ( 'Author URL' , 'activitypub' ),
'value' => get_author_posts_url ( wp_get_current_user () -> ID ),
'private' => true ,
),
2023-10-22 22:20:53 +00:00
'plugin_version' => array (
'label' => __ ( 'Plugin Version' , 'activitypub' ),
'value' => get_plugin_version (),
'private' => true ,
),
2022-12-19 23:08:11 +00:00
),
);
return $info ;
}
2020-04-07 13:03:04 +00:00
}