291 lines
7.5 KiB
PHP
291 lines
7.5 KiB
PHP
<?php
|
|
namespace Activitypub;
|
|
|
|
/**
|
|
* ActivityPub Health_Check Class
|
|
*
|
|
* @author Matthias Pfefferle
|
|
*/
|
|
class Health_Check {
|
|
|
|
/**
|
|
* Initialize health checks
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function init() {
|
|
\add_filter( 'site_status_tests', array( '\Activitypub\Health_Check', 'add_tests' ) );
|
|
}
|
|
|
|
public static function add_tests( $tests ) {
|
|
$tests['direct']['activitypub_test_author_url'] = array(
|
|
'label' => \__( 'Author URL test', 'activitypub' ),
|
|
'test' => array( '\Activitypub\Health_Check', 'test_author_url' ),
|
|
);
|
|
|
|
$tests['direct']['activitypub_test_webfinger'] = array(
|
|
'label' => __( 'WebFinger Test', 'activitypub' ),
|
|
'test' => array( '\Activitypub\Health_Check', 'test_webfinger' ),
|
|
);
|
|
|
|
return $tests;
|
|
}
|
|
|
|
/**
|
|
* Author URL tests
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function test_author_url() {
|
|
$result = array(
|
|
'label' => \__( 'Author URL accessible', 'activitypub' ),
|
|
'status' => 'good',
|
|
'badge' => array(
|
|
'label' => \__( 'ActivityPub', 'activitypub' ),
|
|
'color' => 'green',
|
|
),
|
|
'description' => \sprintf(
|
|
'<p>%s</p>',
|
|
\__( 'Your author URL is accessible and supports the required "Accept" header.', 'activitypub' )
|
|
),
|
|
'actions' => '',
|
|
'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()
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* WebFinger tests
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function test_webfinger() {
|
|
$result = array(
|
|
'label' => \__( 'WebFinger endpoint', 'activitypub' ),
|
|
'status' => 'good',
|
|
'badge' => array(
|
|
'label' => \__( 'ActivityPub', 'activitypub' ),
|
|
'color' => 'green',
|
|
),
|
|
'description' => \sprintf(
|
|
'<p>%s</p>',
|
|
\__( 'Your WebFinger endpoint is accessible and returns the correct informations.', 'activitypub' )
|
|
),
|
|
'actions' => '',
|
|
'test' => 'test_webfinger',
|
|
);
|
|
|
|
$check = self::is_webfinger_endpoint_accessible();
|
|
|
|
if ( true === $check ) {
|
|
return $result;
|
|
}
|
|
|
|
$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()
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Check if `author_posts_url` is accessible and that requerst returns correct JSON
|
|
*
|
|
* @return boolean|WP_Error
|
|
*/
|
|
public static function is_author_url_accessible() {
|
|
$user = \wp_get_current_user();
|
|
$author_url = \get_author_posts_url( $user->ID );
|
|
$reference_author_url = self::get_author_posts_url( $user->ID, $user->user_nicename );
|
|
|
|
// check for "author" in URL
|
|
if ( $author_url !== $reference_author_url ) {
|
|
return new \WP_Error(
|
|
'author_url_not_accessible',
|
|
\sprintf(
|
|
// translators: %s: Author URL
|
|
\__(
|
|
'<p>Your author URL <code>%s</code> was replaced, this is often done by plugins.</p>',
|
|
'activitypub'
|
|
),
|
|
$author_url
|
|
)
|
|
);
|
|
}
|
|
|
|
// try to access author URL
|
|
$response = \wp_remote_get(
|
|
$author_url,
|
|
array(
|
|
'headers' => array( 'Accept' => 'application/activity+json' ),
|
|
'redirection' => 0,
|
|
)
|
|
);
|
|
|
|
if ( \is_wp_error( $response ) ) {
|
|
return new \WP_Error(
|
|
'author_url_not_accessible',
|
|
\sprintf(
|
|
// translators: %s: Author URL
|
|
\__(
|
|
'<p>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.</p>',
|
|
'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 ) ) {
|
|
return new \WP_Error(
|
|
'author_url_not_accessible',
|
|
\sprintf(
|
|
// translators: %s: Author URL
|
|
\__(
|
|
'<p>Your author URL <code>%s</code> is redirecting to another page, this is often done by SEO plugins like "Yoast SEO".</p>',
|
|
'activitypub'
|
|
),
|
|
$author_url
|
|
)
|
|
);
|
|
}
|
|
|
|
// check if response is JSON
|
|
$body = \wp_remote_retrieve_body( $response );
|
|
|
|
if ( ! \is_string( $body ) || ! \is_array( \json_decode( $body, true ) ) ) {
|
|
return new \WP_Error(
|
|
'author_url_not_accessible',
|
|
\sprintf(
|
|
// translators: %s: Author URL
|
|
\__(
|
|
'<p>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.</p>',
|
|
'activitypub'
|
|
),
|
|
$author_url
|
|
)
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if WebFinger endoint is accessible and profile requerst returns correct JSON
|
|
*
|
|
* @return boolean|WP_Error
|
|
*/
|
|
public static function is_webfinger_endpoint_accessible() {
|
|
$user = \wp_get_current_user();
|
|
$webfinger = \Activitypub\get_webfinger_resource( $user->ID );
|
|
|
|
$url = \wp_parse_url( \home_url(), \PHP_URL_SCHEME ) . '://' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
|
|
|
|
if ( \wp_parse_url( \home_url(), \PHP_URL_PORT ) ) {
|
|
$url .= ':' . \wp_parse_url( \home_url(), \PHP_URL_PORT );
|
|
}
|
|
|
|
$url = \trailingslashit( $url ) . '.well-known/webfinger';
|
|
|
|
$url = \add_query_arg( 'resource', 'acct:' . $webfinger, $url );
|
|
|
|
// try to access author URL
|
|
$response = \wp_remote_get(
|
|
$url,
|
|
array(
|
|
'headers' => array( 'Accept' => 'application/activity+json' ),
|
|
'redirection' => 0,
|
|
)
|
|
);
|
|
|
|
if ( \is_wp_error( $response ) ) {
|
|
return new \WP_Error(
|
|
'webfinger_url_not_accessible',
|
|
\sprintf(
|
|
// translators: %s: Author URL
|
|
\__(
|
|
'<p>Your WebFinger endpoint <code>%s</code> is not accessible. Please check your WordPress setup or permalink structure.</p>',
|
|
'activitypub'
|
|
),
|
|
$url
|
|
)
|
|
);
|
|
}
|
|
|
|
$response_code = \wp_remote_retrieve_response_code( $response );
|
|
|
|
// check if response is JSON
|
|
$body = \wp_remote_retrieve_body( $response );
|
|
|
|
if ( ! \is_string( $body ) || ! \is_array( \json_decode( $body, true ) ) ) {
|
|
return new \WP_Error(
|
|
'webfinger_url_not_accessible',
|
|
\sprintf(
|
|
// translators: %s: Author URL
|
|
\__(
|
|
'<p>Your WebFinger endpoint <code>%s</code> does not return valid JSON for <code>application/jrd+json</code>.</p>',
|
|
'activitypub'
|
|
),
|
|
$url
|
|
)
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|