Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion plugins/wpgraphql-logging/phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@
<rule ref="PSR12.Files.DeclareStatement"/>
<rule ref="PEAR.NamingConventions.ValidClassName"/>
<rule ref="PHPCompatibilityWP"/>
<rule ref="WordPress-VIP-Go"/>
<rule ref="WordPress-VIP-Go">
<exclude name="WordPressVIPMinimum.JS.Window"/>
<exclude name="WordPressVIPMinimum.JS.DangerouslySetInnerHTML"/>
<exclude name="WordPressVIPMinimum.JS.InnerHTML"/>
<exclude name="WordPressVIPMinimum.JS.StrippingTags"/>
<exclude name="WordPressVIPMinimum.JS.StringConcat"/>
<exclude name="WordPressVIPMinimum.JS.HTMLExecutingFunctions"/>
</rule>

<rule ref="WordPress">
<exclude name="Universal.Arrays.DisallowShortArraySyntax"/>
Expand Down
105 changes: 105 additions & 0 deletions plugins/wpgraphql-logging/src/Admin/AdminNotice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

namespace WPGraphQL\Logging\Admin;

/**
* The admin notice class for WPGraphQL Logging.
*
* @package WPGraphQL\Logging
*
* @since 0.0.1
*/
class AdminNotice {
/**
* The admin page slug.
*
* @var string
*/
public const ADMIN_NOTICE_KEY = 'wpgraphql-logging-admin-notice';

/**
* The instance of the admin notice.
*
* @var self|null
*/
protected static ?self $instance = null;

/**
* Initializes the view logs page.
*/
public static function init(): ?self {
if ( ! current_user_can( 'manage_options' ) ) {
return null;
}

if ( ! isset( self::$instance ) || ! ( is_a( self::$instance, self::class ) ) ) {
self::$instance = new self();
self::$instance->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 );
}
}
2 changes: 1 addition & 1 deletion plugins/wpgraphql-logging/src/Admin/SettingsPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
* Admin notice template.
*
* @package WPGraphQL\Logging
*
* @since 0.0.1
*/

declare(strict_types=1);

use WPGraphQL\Logging\Admin\AdminNotice;

$wpgraphql_logging_key = AdminNotice::ADMIN_NOTICE_KEY;
$wpgraphql_logging_ajax_nonce = wp_create_nonce( $wpgraphql_logging_key );
$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' );
?>

<div id="<?php echo esc_attr( $wpgraphql_logging_key ); ?>" class="notice wpgraphql-logging-admin-notice notice-warning is-dismissible">
<p><?php echo esc_html( $wpgraphql_logging_notice ); ?></p>
</div>

<script>
window.addEventListener('load', function () {
const dismissBtn = document.querySelector('#<?php echo esc_attr( $wpgraphql_logging_key ); ?>.wpgraphql-logging-admin-notice');
dismissBtn?.addEventListener('click', function (event) {
let postData = new FormData();
postData.append('action', '<?php echo esc_attr( $wpgraphql_logging_key ); ?>');
postData.append('_ajax_nonce', '<?php echo esc_html( $wpgraphql_logging_ajax_nonce ); ?>');

window.fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
method: 'POST',
body: postData,
})
});
});
</script>

<?php
2 changes: 1 addition & 1 deletion plugins/wpgraphql-logging/src/Admin/ViewLogsPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class ViewLogsPage {
/**
* Initializes the view logs page.
*/
public static function init(): ?ViewLogsPage {
public static function init(): ?self {
if ( ! current_user_can( 'manage_options' ) ) {
return null;
}
Expand Down
2 changes: 2 additions & 0 deletions plugins/wpgraphql-logging/src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace WPGraphQL\Logging;

use WPGraphQL\Logging\Admin\AdminNotice;
use WPGraphQL\Logging\Admin\Settings\ConfigurationHelper;
use WPGraphQL\Logging\Admin\SettingsPage;
use WPGraphQL\Logging\Admin\ViewLogsPage;
Expand Down Expand Up @@ -64,6 +65,7 @@ public function setup(): void {
ConfigurationHelper::register_cache_hooks(); // Register cache hooks.
SettingsPage::init(); // Settings page.
ViewLogsPage::init(); // View logs page.
AdminNotice::init(); // Admin notices.
}

do_action( 'wpgraphql_logging_plugin_setup', self::$instance );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
// Clear logs table
$table_name = $wpdb->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');
}
});
});
33 changes: 33 additions & 0 deletions plugins/wpgraphql-logging/tests/e2e/specs/admin-notice.spec.js
Original file line number Diff line number Diff line change
@@ -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();
});
});
124 changes: 124 additions & 0 deletions plugins/wpgraphql-logging/tests/wpunit/Admin/AdminNoticeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

declare(strict_types=1);

namespace WPGraphQL\Logging\Tests\Admin;

use lucatume\WPBrowser\TestCase\WPTestCase;
use WPGraphQL\Logging\Admin\AdminNotice;
use ReflectionClass;
use Mockery;


/**
* Test class for AdminNotice.
*
* @package WPGraphQL\Logging
*
* @since 0.0.1
*/
class AdminNoticeTest extends WPTestCase {

public function set_as_admin(): void {
$admin_user = $this->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('<script>', $output);
}

public function test_dismiss_admin_notice_updates_user_meta(): void {
$this->set_as_admin();

$notice = AdminNotice::init();
$this->assertFalse($notice->is_notice_dismissed());

// Dismiss the notice.
AdminNotice::dismiss_admin_notice();
$this->assertTrue($notice->is_notice_dismissed());
}

public function test_process_ajax_request_dismisses_notice(): void {
$this->set_as_admin();
$this->unset_dismissed_notice_meta_data();

$notice = AdminNotice::init();
$this->assertFalse($notice->is_notice_dismissed());

$notice::dismiss_admin_notice();
$this->assertTrue($notice->is_notice_dismissed());
}

}
Loading