diff --git a/src/alley/wp/alleyvate/features/class-site-health.php b/src/alley/wp/alleyvate/features/class-site-health.php new file mode 100644 index 00000000..2a23f43d --- /dev/null +++ b/src/alley/wp/alleyvate/features/class-site-health.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @package wp-alleyvate + */ + +namespace Alley\WP\Alleyvate\Features; + +use Alley\WP\Alleyvate\Feature; + +use function Alley\WP\Alleyvate\available_features; +use function Alley\WP\Alleyvate\should_load_feature; + +/** + * Site Health feature. + */ +final class Site_Health implements Feature { + /** + * Boot the feature. + */ + public function boot(): void { + add_filter( 'debug_information', [ $this, 'add_debug_information' ] ); + } + + /** + * Add debug information to the Site Health screen. + * + * @param array $info Debug information. + * @return array + */ + public function add_debug_information( $info ): array { + if ( ! \is_array( $info ) ) { + $info = []; + } + + $info['wp-alleyvate'] = [ + 'label' => __( 'Alleyvate', 'alley' ), + 'description' => __( 'Diagnostic information about the Alleyvate plugin and which features are enabled.', 'alley' ), + 'fields' => array_map( + fn ( string $handle ) => [ + 'label' => sprintf( + /* translators: %s: Feature name. */ + __( 'Feature: %s', 'alley' ), + $handle, + ), + 'value' => should_load_feature( $handle ) ? __( 'Enabled', 'alley' ) : __( 'Disabled', 'alley' ), + ], + array_keys( available_features() ), + ), + ]; + + return $info; + } +} diff --git a/src/alley/wp/alleyvate/load.php b/src/alley/wp/alleyvate/load.php index a0430128..1fe639a0 100644 --- a/src/alley/wp/alleyvate/load.php +++ b/src/alley/wp/alleyvate/load.php @@ -13,20 +13,12 @@ namespace Alley\WP\Alleyvate; /** - * Load plugin features. + * Get the available features to load. + * + * @return array */ -function load(): void { - // Bail if the Alleyvate feature interface isn't loaded to prevent a fatal error. - if ( ! interface_exists( Feature::class ) ) { - return; - } - - /** - * Features to load. - * - * @var Feature[] $features - */ - $features = [ +function available_features(): array { + return [ 'clean_admin_bar' => new Features\Clean_Admin_Bar(), 'disable_comments' => new Features\Disable_Comments(), 'disable_dashboard_widgets' => new Features\Disable_Dashboard_Widgets(), @@ -34,31 +26,52 @@ function load(): void { 'disable_trackbacks' => new Features\Disable_Trackbacks(), 'disallow_file_edit' => new Features\Disallow_File_Edit(), 'redirect_guess_shortcircuit' => new Features\Redirect_Guess_Shortcircuit(), + 'site_health' => new Features\Site_Health(), 'user_enumeration_restrictions' => new Features\User_Enumeration_Restrictions(), ]; +} - foreach ( $features as $handle => $feature ) { - $load = true; +/** + * Determine whether to load a feature. + * + * @param string $handle Feature handle. + * @return bool + */ +function should_load_feature( string $handle ): bool { + $load = true; - /** - * Filters whether to load an Alleyvate feature. - * - * @param bool $load Whether to load the feature. Default true. - * @param string $handle Feature handle. - */ - $load = apply_filters( 'alleyvate_load_feature', $load, $handle ); + /** + * Filters whether to load an Alleyvate feature. + * + * @param bool $load Whether to load the feature. Default true. + * @param string $handle Feature handle. + */ + $load = apply_filters( 'alleyvate_load_feature', $load, $handle ); + + /** + * Filters whether to load the given Alleyvate feature. + * + * The dynamic portion of the hook name, `$handle`, refers to the + * machine name for the feature. + * + * @param bool $load Whether to load the feature. Default true. + */ + $load = apply_filters( "alleyvate_load_{$handle}", $load ); + + return (bool) $load; +} - /** - * Filters whether to load the given Alleyvate feature. - * - * The dynamic portion of the hook name, `$handle`, refers to the - * machine name for the feature. - * - * @param bool $load Whether to load the feature. Default true. - */ - $load = apply_filters( "alleyvate_load_{$handle}", $load ); +/** + * Load plugin features. + */ +function load(): void { + // Bail if the Alleyvate feature interface isn't loaded to prevent a fatal error. + if ( ! interface_exists( Feature::class ) ) { + return; + } - if ( $load ) { + foreach ( available_features() as $handle => $feature ) { + if ( should_load_feature( $handle ) ) { $feature->boot(); } } diff --git a/tests/alley/wp/alleyvate/features/test-site-health.php b/tests/alley/wp/alleyvate/features/test-site-health.php new file mode 100644 index 00000000..40fcee1c --- /dev/null +++ b/tests/alley/wp/alleyvate/features/test-site-health.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @package wp-alleyvate + */ + +namespace Alley\WP\Alleyvate\Features; + +use Alley\WP\Alleyvate\Feature; +use Mantle\Testkit\Test_Case; + +use function Alley\WP\Alleyvate\available_features; + +/** + * Test for site health feature. + */ +final class Test_Site_Health extends Test_Case { + /** + * Feature instance. + * + * @var Feature + */ + private Feature $feature; + + /** + * Set up. + */ + protected function setUp(): void { + parent::setUp(); + + $this->feature = new Site_Health(); + } + + /** + * Test the site health feature. + */ + public function test_site_health_feature() { + $features = available_features(); + + $this->expectApplied( 'alleyvate_load_feature' )->times( \count( $features ) ); + + foreach ( $features as $handle => $class ) { + $this->expectApplied( "alleyvate_load_{$handle}" )->once(); + } + + $this->feature->boot(); + + // Mock the Site Health screen. + $data = apply_filters( 'debug_information', [] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + + $this->assertNotEmpty( $data['wp-alleyvate'] ?? null ); + $this->assertNotEmpty( $data['wp-alleyvate']['fields'] ?? null ); + $this->assertCount( \count( $features ), $data['wp-alleyvate']['fields'] ); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a01ce7b3..11baec86 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,6 +14,8 @@ // Fires on 'muplugins_loaded'. ->loaded( function () { + require_once __DIR__ . '/../wp-alleyvate.php'; + /* * Turn off all features by default so that we can verify that the behavior * of WordPress changes after we turn the feature on.