diff --git a/plugins/wpgraphql-logging/phpcs.xml b/plugins/wpgraphql-logging/phpcs.xml index 82c557f3..77b82827 100644 --- a/plugins/wpgraphql-logging/phpcs.xml +++ b/plugins/wpgraphql-logging/phpcs.xml @@ -54,7 +54,14 @@ - + + + + + + + + diff --git a/plugins/wpgraphql-logging/src/Admin/AdminNotice.php b/plugins/wpgraphql-logging/src/Admin/AdminNotice.php new file mode 100644 index 00000000..ecda3e74 --- /dev/null +++ b/plugins/wpgraphql-logging/src/Admin/AdminNotice.php @@ -0,0 +1,105 @@ +setup(); + } + + do_action( 'wpgraphql_logging_admin_notice_init', self::$instance ); + + return self::$instance; + } + + /** + * Setup the admin notice. + */ + public function setup(): void { + $key = self::ADMIN_NOTICE_KEY; + $is_dismissed = $this->is_notice_dismissed(); + + // Exit if the notice has been dismissed. + if ( $is_dismissed ) { + return; + } + + add_action( 'admin_notices', [ $this, 'register_admin_notice' ], 10, 0 ); + add_action( 'wp_ajax_' . $key, [ $this, 'process_ajax_request' ], 10, 0 ); + } + + /** + * Register admin notice to inform users about WPGraphQL Logging. + */ + public function register_admin_notice(): void { + $template = __DIR__ . '/View/Templates/WPGraphQLLoggerNotice.php'; + + if ( ! file_exists( $template ) ) { + return; + } + + require $template; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable + } + + /** + * Process the AJAX request. + */ + public function process_ajax_request(): void { + $key = self::ADMIN_NOTICE_KEY; + if ( ! isset( $_POST['action'] ) || esc_attr( $key ) !== $_POST['action'] ) { + return; + } + + check_ajax_referer( $key ); + + self::dismiss_admin_notice(); + } + + /** + * Check if the admin notice is dismissed. + */ + public function is_notice_dismissed(): bool { + $key = self::ADMIN_NOTICE_KEY; + return (bool) get_user_meta( get_current_user_id(), $key, true ); + } + + /** + * Dismiss the admin notice. + */ + public static function dismiss_admin_notice(): void { + $key = self::ADMIN_NOTICE_KEY; + update_user_meta( get_current_user_id(), $key, 1 ); + } +} diff --git a/plugins/wpgraphql-logging/src/Admin/SettingsPage.php b/plugins/wpgraphql-logging/src/Admin/SettingsPage.php index 558c1f7b..2b8ecd6c 100644 --- a/plugins/wpgraphql-logging/src/Admin/SettingsPage.php +++ b/plugins/wpgraphql-logging/src/Admin/SettingsPage.php @@ -43,7 +43,7 @@ class SettingsPage { /** * Initializes the settings page. */ - public static function init(): ?SettingsPage { + public static function init(): ?self { if ( ! current_user_can( 'manage_options' ) ) { return null; } diff --git a/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php b/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php new file mode 100644 index 00000000..834b3957 --- /dev/null +++ b/plugins/wpgraphql-logging/src/Admin/View/Templates/WPGraphQLLoggerNotice.php @@ -0,0 +1,39 @@ + + +
+

+
+ + + +prefix . 'wpgraphql_logging'; $wpdb->query("TRUNCATE TABLE {$table_name}"); + + + // Remove admin notice dismissed meta data + delete_user_meta(get_current_user_id(), 'wpgraphql-logging-admin-notice'); } -}); \ No newline at end of file +}); diff --git a/plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js b/plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js new file mode 100644 index 00000000..6235c7b8 --- /dev/null +++ b/plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js @@ -0,0 +1,33 @@ +import { expect, test } from "@wordpress/e2e-test-utils-playwright"; +import { + resetPluginSettings, + goToLoggingSettingsPage, +} from "../utils"; + +test.describe("WPGraphQL Logging Admin Notice", () => { + test.beforeEach(async ({ admin, page }) => { + await resetPluginSettings(admin); // Reset user meta data + await goToLoggingSettingsPage(admin); + await expect(page.locator("h1")).toHaveText("WPGraphQL Logging Settings"); + }); + + test("admin notice is displayed", async ({ + page, + admin, + }) => { + await goToLoggingSettingsPage(admin); + await expect( + page.locator("#wpgraphql-logging-admin-notice") + ).toBeVisible(); + + await page.locator("#wpgraphql-logging-admin-notice.notice .notice-dismiss").click(); + await expect( + page.locator("#wpgraphql-logging-admin-notice"), + ).not.toBeVisible(); + + await page.reload(); + await expect( + page.locator("#wpgraphql-logging-admin-notice"), + ).not.toBeVisible(); + }); +}); diff --git a/plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php b/plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php new file mode 100644 index 00000000..9cfd6f42 --- /dev/null +++ b/plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php @@ -0,0 +1,124 @@ +factory->user->create(['role' => 'administrator']); + wp_set_current_user($admin_user); + set_current_screen('dashboard'); + } + + public function set_dismissed_notice_meta_data(): void { + update_user_meta( get_current_user_id(), AdminNotice::ADMIN_NOTICE_KEY, 1 ); + } + + public function unset_dismissed_notice_meta_data(): void { + delete_user_meta( get_current_user_id(), AdminNotice::ADMIN_NOTICE_KEY ); + } + + public function test_admin_notice_instance() { + $this->set_as_admin(); + + $reflection = new ReflectionClass( AdminNotice::class ); + $instanceProperty = $reflection->getProperty( 'instance' ); + $instanceProperty->setAccessible( true ); + $instanceProperty->setValue( null ); + + $this->assertNull( $instanceProperty->getValue() ); + $instance = AdminNotice::init(); + + $this->assertInstanceOf( AdminNotice::class, $instanceProperty->getValue() ); + $this->assertSame( $instance, $instanceProperty->getValue(), 'AdminNotice::init() should set the static instance property' ); + } + + public function test_initialization_sets_instance_with_no_hooks_registered_as_not_admin(): void { + wp_set_current_user(0); + $instance = AdminNotice::init(); + $this->assertFalse(has_action('admin_notices', [$instance, 'register_admin_notice'])); + $this->assertFalse(has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$instance, 'process_ajax_request'])); + } + + public function test_initialization_sets_instance_with_no_hooks_registered_as_dismissed(): void { + $this->set_as_admin(); + $this->set_dismissed_notice_meta_data(); + + $instance = AdminNotice::init(); + $this->assertFalse(has_action('admin_notices', [$instance, 'register_admin_notice'])); + $this->assertFalse(has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$instance, 'process_ajax_request'])); + } + + public function test_check_notice_is_displayed(): void { + $this->set_as_admin(); + + $reflection = new ReflectionClass( AdminNotice::class ); + $instanceProperty = $reflection->getProperty( 'instance' ); + $instanceProperty->setAccessible( true ); + $instanceProperty->setValue( null ); + + $notice = AdminNotice::init(); // Notice should be now registered + + $hook = has_action('admin_notices', [$notice, 'register_admin_notice']); + $this->assertNotFalse($hook); + $this->assertEquals(10, $hook); + + $ajax_hook = has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$notice, 'process_ajax_request']); + $this->assertNotFalse($ajax_hook); + $this->assertEquals(10, $ajax_hook); + } + + public function test_register_admin_notice_outputs_template(): void { + $this->set_as_admin(); + + $notice = AdminNotice::init(); + + ob_start(); + $notice->register_admin_notice(); + $output = ob_get_clean(); + + $this->assertStringContainsString('wpgraphql-logging-admin-notice', $output); + $this->assertStringContainsString('notice-warning', $output); + $this->assertStringContainsString('is-dismissible', $output); + $this->assertStringContainsString('Heads up! While very useful for debugging', $output); + $this->assertStringContainsString('