Skip to content

Commit ea4a066

Browse files
authored
Merge pull request #478 from wpengine/chore-add-admin-notice-logging-plugin
chore: Add admin notice to WPGraphQL Logging Plugin
2 parents b193640 + c819181 commit ea4a066

File tree

9 files changed

+318
-4
lines changed

9 files changed

+318
-4
lines changed

plugins/wpgraphql-logging/phpcs.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@
5454
<rule ref="PSR12.Files.DeclareStatement"/>
5555
<rule ref="PEAR.NamingConventions.ValidClassName"/>
5656
<rule ref="PHPCompatibilityWP"/>
57-
<rule ref="WordPress-VIP-Go"/>
57+
<rule ref="WordPress-VIP-Go">
58+
<exclude name="WordPressVIPMinimum.JS.Window"/>
59+
<exclude name="WordPressVIPMinimum.JS.DangerouslySetInnerHTML"/>
60+
<exclude name="WordPressVIPMinimum.JS.InnerHTML"/>
61+
<exclude name="WordPressVIPMinimum.JS.StrippingTags"/>
62+
<exclude name="WordPressVIPMinimum.JS.StringConcat"/>
63+
<exclude name="WordPressVIPMinimum.JS.HTMLExecutingFunctions"/>
64+
</rule>
5865

5966
<rule ref="WordPress">
6067
<exclude name="Universal.Arrays.DisallowShortArraySyntax"/>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WPGraphQL\Logging\Admin;
6+
7+
/**
8+
* The admin notice class for WPGraphQL Logging.
9+
*
10+
* @package WPGraphQL\Logging
11+
*
12+
* @since 0.0.1
13+
*/
14+
class AdminNotice {
15+
/**
16+
* The admin page slug.
17+
*
18+
* @var string
19+
*/
20+
public const ADMIN_NOTICE_KEY = 'wpgraphql-logging-admin-notice';
21+
22+
/**
23+
* The instance of the admin notice.
24+
*
25+
* @var self|null
26+
*/
27+
protected static ?self $instance = null;
28+
29+
/**
30+
* Initializes the view logs page.
31+
*/
32+
public static function init(): ?self {
33+
if ( ! current_user_can( 'manage_options' ) ) {
34+
return null;
35+
}
36+
37+
if ( ! isset( self::$instance ) || ! ( is_a( self::$instance, self::class ) ) ) {
38+
self::$instance = new self();
39+
self::$instance->setup();
40+
}
41+
42+
do_action( 'wpgraphql_logging_admin_notice_init', self::$instance );
43+
44+
return self::$instance;
45+
}
46+
47+
/**
48+
* Setup the admin notice.
49+
*/
50+
public function setup(): void {
51+
$key = self::ADMIN_NOTICE_KEY;
52+
$is_dismissed = $this->is_notice_dismissed();
53+
54+
// Exit if the notice has been dismissed.
55+
if ( $is_dismissed ) {
56+
return;
57+
}
58+
59+
add_action( 'admin_notices', [ $this, 'register_admin_notice' ], 10, 0 );
60+
add_action( 'wp_ajax_' . $key, [ $this, 'process_ajax_request' ], 10, 0 );
61+
}
62+
63+
/**
64+
* Register admin notice to inform users about WPGraphQL Logging.
65+
*/
66+
public function register_admin_notice(): void {
67+
$template = __DIR__ . '/View/Templates/WPGraphQLLoggerNotice.php';
68+
69+
if ( ! file_exists( $template ) ) {
70+
return;
71+
}
72+
73+
require $template; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
74+
}
75+
76+
/**
77+
* Process the AJAX request.
78+
*/
79+
public function process_ajax_request(): void {
80+
$key = self::ADMIN_NOTICE_KEY;
81+
if ( ! isset( $_POST['action'] ) || esc_attr( $key ) !== $_POST['action'] ) {
82+
return;
83+
}
84+
85+
check_ajax_referer( $key );
86+
87+
self::dismiss_admin_notice();
88+
}
89+
90+
/**
91+
* Check if the admin notice is dismissed.
92+
*/
93+
public function is_notice_dismissed(): bool {
94+
$key = self::ADMIN_NOTICE_KEY;
95+
return (bool) get_user_meta( get_current_user_id(), $key, true );
96+
}
97+
98+
/**
99+
* Dismiss the admin notice.
100+
*/
101+
public static function dismiss_admin_notice(): void {
102+
$key = self::ADMIN_NOTICE_KEY;
103+
update_user_meta( get_current_user_id(), $key, 1 );
104+
}
105+
}

plugins/wpgraphql-logging/src/Admin/SettingsPage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class SettingsPage {
4343
/**
4444
* Initializes the settings page.
4545
*/
46-
public static function init(): ?SettingsPage {
46+
public static function init(): ?self {
4747
if ( ! current_user_can( 'manage_options' ) ) {
4848
return null;
4949
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
/**
3+
* Admin notice template.
4+
*
5+
* @package WPGraphQL\Logging
6+
*
7+
* @since 0.0.1
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
use WPGraphQL\Logging\Admin\AdminNotice;
13+
14+
$wpgraphql_logging_key = AdminNotice::ADMIN_NOTICE_KEY;
15+
$wpgraphql_logging_ajax_nonce = wp_create_nonce( $wpgraphql_logging_key );
16+
$wpgraphql_logging_notice = __( "Heads up! While very useful for debugging, the WPGraphQL Logging Plugin can impact your site's performance under heavy usage, so please use it judiciously.", 'wpgraphql-logging' );
17+
?>
18+
19+
<div id="<?php echo esc_attr( $wpgraphql_logging_key ); ?>" class="notice wpgraphql-logging-admin-notice notice-warning is-dismissible">
20+
<p><?php echo esc_html( $wpgraphql_logging_notice ); ?></p>
21+
</div>
22+
23+
<script>
24+
window.addEventListener('load', function () {
25+
const dismissBtn = document.querySelector('#<?php echo esc_attr( $wpgraphql_logging_key ); ?>.wpgraphql-logging-admin-notice');
26+
dismissBtn?.addEventListener('click', function (event) {
27+
let postData = new FormData();
28+
postData.append('action', '<?php echo esc_attr( $wpgraphql_logging_key ); ?>');
29+
postData.append('_ajax_nonce', '<?php echo esc_html( $wpgraphql_logging_ajax_nonce ); ?>');
30+
31+
window.fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
32+
method: 'POST',
33+
body: postData,
34+
})
35+
});
36+
});
37+
</script>
38+
39+
<?php

plugins/wpgraphql-logging/src/Admin/ViewLogsPage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class ViewLogsPage {
5555
/**
5656
* Initializes the view logs page.
5757
*/
58-
public static function init(): ?ViewLogsPage {
58+
public static function init(): ?self {
5959
if ( ! current_user_can( 'manage_options' ) ) {
6060
return null;
6161
}

plugins/wpgraphql-logging/src/Plugin.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace WPGraphQL\Logging;
66

7+
use WPGraphQL\Logging\Admin\AdminNotice;
78
use WPGraphQL\Logging\Admin\Settings\ConfigurationHelper;
89
use WPGraphQL\Logging\Admin\Settings\Fields\Tab\BasicConfigurationTab;
910
use WPGraphQL\Logging\Admin\Settings\Fields\Tab\DataManagementTab;
@@ -67,6 +68,7 @@ public function setup(): void {
6768
ConfigurationHelper::register_cache_hooks(); // Register cache hooks.
6869
SettingsPage::init(); // Settings page.
6970
ViewLogsPage::init(); // View logs page.
71+
AdminNotice::init(); // Admin notices.
7072
}
7173

7274
do_action( 'wpgraphql_logging_plugin_setup', self::$instance );

plugins/wpgraphql-logging/tests/e2e/plugins/reset-wpgraphql-logging-settings/reset-wpgraphql-logging-settings.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@
1414
// Clear logs table
1515
$table_name = $wpdb->prefix . 'wpgraphql_logging';
1616
$wpdb->query("TRUNCATE TABLE {$table_name}");
17+
18+
19+
// Remove admin notice dismissed meta data
20+
delete_user_meta(get_current_user_id(), 'wpgraphql-logging-admin-notice');
1721
}
18-
});
22+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect, test } from "@wordpress/e2e-test-utils-playwright";
2+
import {
3+
resetPluginSettings,
4+
goToLoggingSettingsPage,
5+
} from "../utils";
6+
7+
test.describe("WPGraphQL Logging Admin Notice", () => {
8+
test.beforeEach(async ({ admin, page }) => {
9+
await resetPluginSettings(admin); // Reset user meta data
10+
await goToLoggingSettingsPage(admin);
11+
await expect(page.locator("h1")).toHaveText("WPGraphQL Logging Settings");
12+
});
13+
14+
test("admin notice is displayed", async ({
15+
page,
16+
admin,
17+
}) => {
18+
await goToLoggingSettingsPage(admin);
19+
await expect(
20+
page.locator("#wpgraphql-logging-admin-notice")
21+
).toBeVisible();
22+
23+
await page.locator("#wpgraphql-logging-admin-notice.notice .notice-dismiss").click();
24+
await expect(
25+
page.locator("#wpgraphql-logging-admin-notice"),
26+
).not.toBeVisible();
27+
28+
await page.reload();
29+
await expect(
30+
page.locator("#wpgraphql-logging-admin-notice"),
31+
).not.toBeVisible();
32+
});
33+
});
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WPGraphQL\Logging\Tests\Admin;
6+
7+
use lucatume\WPBrowser\TestCase\WPTestCase;
8+
use WPGraphQL\Logging\Admin\AdminNotice;
9+
use ReflectionClass;
10+
use Mockery;
11+
12+
13+
/**
14+
* Test class for AdminNotice.
15+
*
16+
* @package WPGraphQL\Logging
17+
*
18+
* @since 0.0.1
19+
*/
20+
class AdminNoticeTest extends WPTestCase {
21+
22+
public function set_as_admin(): void {
23+
$admin_user = $this->factory->user->create(['role' => 'administrator']);
24+
wp_set_current_user($admin_user);
25+
set_current_screen('dashboard');
26+
}
27+
28+
public function set_dismissed_notice_meta_data(): void {
29+
update_user_meta( get_current_user_id(), AdminNotice::ADMIN_NOTICE_KEY, 1 );
30+
}
31+
32+
public function unset_dismissed_notice_meta_data(): void {
33+
delete_user_meta( get_current_user_id(), AdminNotice::ADMIN_NOTICE_KEY );
34+
}
35+
36+
public function test_admin_notice_instance() {
37+
$this->set_as_admin();
38+
39+
$reflection = new ReflectionClass( AdminNotice::class );
40+
$instanceProperty = $reflection->getProperty( 'instance' );
41+
$instanceProperty->setAccessible( true );
42+
$instanceProperty->setValue( null );
43+
44+
$this->assertNull( $instanceProperty->getValue() );
45+
$instance = AdminNotice::init();
46+
47+
$this->assertInstanceOf( AdminNotice::class, $instanceProperty->getValue() );
48+
$this->assertSame( $instance, $instanceProperty->getValue(), 'AdminNotice::init() should set the static instance property' );
49+
}
50+
51+
public function test_initialization_sets_instance_with_no_hooks_registered_as_not_admin(): void {
52+
wp_set_current_user(0);
53+
$instance = AdminNotice::init();
54+
$this->assertFalse(has_action('admin_notices', [$instance, 'register_admin_notice']));
55+
$this->assertFalse(has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$instance, 'process_ajax_request']));
56+
}
57+
58+
public function test_initialization_sets_instance_with_no_hooks_registered_as_dismissed(): void {
59+
$this->set_as_admin();
60+
$this->set_dismissed_notice_meta_data();
61+
62+
$instance = AdminNotice::init();
63+
$this->assertFalse(has_action('admin_notices', [$instance, 'register_admin_notice']));
64+
$this->assertFalse(has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$instance, 'process_ajax_request']));
65+
}
66+
67+
public function test_check_notice_is_displayed(): void {
68+
$this->set_as_admin();
69+
70+
$reflection = new ReflectionClass( AdminNotice::class );
71+
$instanceProperty = $reflection->getProperty( 'instance' );
72+
$instanceProperty->setAccessible( true );
73+
$instanceProperty->setValue( null );
74+
75+
$notice = AdminNotice::init(); // Notice should be now registered
76+
77+
$hook = has_action('admin_notices', [$notice, 'register_admin_notice']);
78+
$this->assertNotFalse($hook);
79+
$this->assertEquals(10, $hook);
80+
81+
$ajax_hook = has_action('wp_ajax_' . AdminNotice::ADMIN_NOTICE_KEY, [$notice, 'process_ajax_request']);
82+
$this->assertNotFalse($ajax_hook);
83+
$this->assertEquals(10, $ajax_hook);
84+
}
85+
86+
public function test_register_admin_notice_outputs_template(): void {
87+
$this->set_as_admin();
88+
89+
$notice = AdminNotice::init();
90+
91+
ob_start();
92+
$notice->register_admin_notice();
93+
$output = ob_get_clean();
94+
95+
$this->assertStringContainsString('wpgraphql-logging-admin-notice', $output);
96+
$this->assertStringContainsString('notice-warning', $output);
97+
$this->assertStringContainsString('is-dismissible', $output);
98+
$this->assertStringContainsString('Heads up! While very useful for debugging', $output);
99+
$this->assertStringContainsString('<script>', $output);
100+
}
101+
102+
public function test_dismiss_admin_notice_updates_user_meta(): void {
103+
$this->set_as_admin();
104+
105+
$notice = AdminNotice::init();
106+
$this->assertFalse($notice->is_notice_dismissed());
107+
108+
// Dismiss the notice.
109+
AdminNotice::dismiss_admin_notice();
110+
$this->assertTrue($notice->is_notice_dismissed());
111+
}
112+
113+
public function test_process_ajax_request_dismisses_notice(): void {
114+
$this->set_as_admin();
115+
$this->unset_dismissed_notice_meta_data();
116+
117+
$notice = AdminNotice::init();
118+
$this->assertFalse($notice->is_notice_dismissed());
119+
120+
$notice::dismiss_admin_notice();
121+
$this->assertTrue($notice->is_notice_dismissed());
122+
}
123+
124+
}

0 commit comments

Comments
 (0)