Skip to content

Commit

Permalink
Merge 77d1917 into 49e2d5a
Browse files Browse the repository at this point in the history
  • Loading branch information
josephfusco committed Jan 31, 2024
2 parents 49e2d5a + 77d1917 commit 2d1bc8b
Show file tree
Hide file tree
Showing 3 changed files with 294 additions and 2 deletions.
17 changes: 15 additions & 2 deletions access-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ static function ( \WPGraphQL\Admin\Settings\SettingsRegistry $registry ) use ( $
*
* @param mixed|string|mixed[] $message The debug message
* @param array<string,mixed> $config The debug config. Should be an associative array of keys and values.
* $config['type'] will set the "type" of the log, default type is GRAPHQL_DEBUG.
* $config['type'] will set the "type" of the log, default type is GRAPHQL_DEBUG.
* Other fields added to $config will be merged into the debug entry.
*
* @return void
Expand Down Expand Up @@ -819,7 +819,7 @@ function get_graphql_setting( string $option_name, $default_value = '', $section

/**
* Filter the section fields
* @param array<string,mixed> $section_fields The values of the fields stored for the section
* @param string $section_name The name of the section
* @param mixed $default_value The default value for the option being retrieved
Expand Down Expand Up @@ -953,3 +953,16 @@ function str_ends_with( string $haystack, string $needle ): bool {
return $needle_length <= strlen( $haystack ) && 0 === substr_compare( $haystack, $needle, -$needle_length );
}
}

/**
* @param string $slug A unique slug to identify the admin notice by
* @param array<mixed> $config The config for the admin notice. Determines visibility, context, etc.
*/
function register_graphql_admin_notice( string $slug, array $config ): void {
add_action(
'graphql_admin_notices_init',
static function ( \WPGraphQL\Admin\AdminNotices $admin_notices ) use ( $slug, $config ) {
$admin_notices->add_admin_notice( $slug, $config );
}
);
}
3 changes: 3 additions & 0 deletions src/Admin/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public function init() {
$this->admin_enabled = apply_filters( 'graphql_show_admin', true );
$this->graphiql_enabled = apply_filters( 'graphql_enable_graphiql', get_graphql_setting( 'graphiql_enabled', true ) );

$admin_notices = new AdminNotices();
$admin_notices->init();

// This removes the menu page for WPGraphiQL as it's now built into WPGraphQL
if ( $this->graphiql_enabled ) {
add_action(
Expand Down
276 changes: 276 additions & 0 deletions src/Admin/AdminNotices.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
<?php

namespace WPGraphQL\Admin;

/**
* This class isn't intended for direct extending or customizing.
*
* This class is responsible for handling the management and display of admin notices
* related directly to WPGraphQL.
*
* Breaking changes to this class will not be considered a semver breaking change as there's no
* expectation that users will be calling these functions directly or extending this class.
*/
final class AdminNotices {

/**
* Stores the admin notices to display
*
* @var array<mixed>
*/
protected $admin_notices = [];

/**
* @var array<string>
*/
protected $dismissed_notices = [];

/**
* Initialize the Admin Notices class
*/
public function init(): void {

$this->admin_notices = [
'wpgraphql-gravity-forms' => [
'type' => 'danger',
'message' => __( 'You are using WPGraphQL and Advanced Custom Fields. Have you seen the new <a href="https://acf.wpgraphql.com/" target="_blank" rel="nofollow">WPGraphQL for ACF</a>?', 'wp-graphql' ),
'is_dismissable' => false,
],
'wpgraphql-acf-announcement' => [
'type' => 'warning',
'message' => __( 'You are using WPGraphQL and Advanced Custom Fields. Have you seen the new <a href="https://acf.wpgraphql.com/" target="_blank" rel="nofollow">WPGraphQL for ACF</a>?', 'wp-graphql' ),
'is_dismissable' => true,
'conditions' => function() {
return $this->should_display_acf_notice();
}
],
];

$this->dismissed_notices = get_option( 'wpgraphql_dismissed_admin_notices', [] );
$this->pre_filter_dismissed_notices();

do_action( 'graphql_admin_notices_init', $this );
add_action( 'admin_notices', [ $this, 'maybe_display_notices' ] );
add_action( 'admin_init', [ $this, 'handle_dismissal_of_acf_notice' ] );
}

/**
* Pre-filters dismissed notices from the admin notices array.
*/
protected function pre_filter_dismissed_notices(): void {

// remove any notice that's been dismissed
foreach ( $this->dismissed_notices as $dismissed_notice ) {
unset( $this->admin_notices[ $dismissed_notice ] );
}

// For all remaining notices, run the callback to see if it's actually relevant
foreach ( $this->admin_notices as $notice_slug => $notice ) {

if ( ! isset( $notice['conditions'] ) ) {
continue;
}

if ( ! is_callable( $notice['conditions'] ) ) {
continue;
}

if ( false === $notice['conditions']() ) {
unset( $this->admin_notices[ $notice_slug ] );
}

}
}

/**
* Return all admin notices
*
* @return array<mixed>
*/
public function get_admin_notices(): array {
return $this->admin_notices;
}

/**
* @param string $slug Return the admin notice corresponding with the given slug
*
* @return array<mixed>
*/
public function get_admin_notice( string $slug ): array {
$notices = $this->get_admin_notices();
return $notices[ $slug ] ?? [];
}

/**
* @param string $slug The slug identifying the admin notice
* @param array<mixed> $config The config of the admin notice
*
* @return array<mixed>
*/
public function add_admin_notice( string $slug, array $config ): array {
$this->admin_notices[ $slug ] = $config;
return $this->admin_notices[ $slug ];
}

/**
* Given the slug of an admin notice, remove it from the notices
*
* @param string $slug The slug identifying the admin notice to remove
*
* @return array<mixed>
*/
public function remove_admin_notice( string $slug ): array {
unset( $this->admin_notices[ $slug ] );
return $this->admin_notices;
}

/**
* Determine whether a notice is dismissable or not
*
* @param array<mixed> $notice The notice to check whether its dismissable or not
*/
public function is_notice_dismissable( array $notice = [] ): bool {
return ( ! isset( $notice['is_dismissable'] ) || false !== (bool) $notice['is_dismissable'] );
}

/**
* Display notices if they are displayable
*/
public function maybe_display_notices(): void {
if ( ! $this->is_plugin_scoped_page() ) {
return;
}

$this->render_notices();
}

/**
* @return void
*/
protected function render_notices(): void {
$notices = $this->get_admin_notices();

if ( empty( $notices ) ) {
return;
}

?>
<style>
/* Only display the ACF notice */
body.toplevel_page_graphiql-ide #wpbody .wpgraphql-admin-notice {
display: block;
position: absolute;
top: 0;
right: 0;
z-index: 1;
min-width: 40%;
}
body.toplevel_page_graphiql-ide #wpbody #wp-graphiql-wrapper {
margin-top: <?php echo count( $notices ) * 45; ?>px;
}
.wpgraphql-admin-notice {
position: relative;
text-decoration: none;
}
.wpgraphql-admin-notice .notice-dismiss {
text-decoration: none;
}

</style>
<?php
$count = 0;
foreach ( $notices as $notice_slug => $notice ) {
?>
<style>
body.toplevel_page_graphiql-ide #wpbody #wpgraphql-admin-notice-<?php echo esc_attr( $notice_slug ); ?> {
top: <?php echo esc_attr( ( $count * 45 ) . 'px' ); ?>
}
</style>
<div id="wpgraphql-admin-notice-<?php echo esc_attr( $notice_slug ); ?>" class="wpgraphql-admin-notice notice notice-<?php echo esc_attr( $notice['type'] ) ?? 'success'; ?> <?php echo $this->is_notice_dismissable( $notice ) ? 'is-dismissable' : ''; ?>">
<p><?php echo ! empty( $notice['message'] ) ? wp_kses_post( $notice['message'] ) : ''; ?></p>
<?php
if ( $this->is_notice_dismissable( $notice ) ) {
$dismiss_acf_nonce = wp_create_nonce( 'wpgraphql_disable_acf_notice_nonce' );
$dismiss_url = add_query_arg(
[
'wpgraphql_disable_acf_notice_nonce' => $dismiss_acf_nonce,
'wpgraphql_disable_notice' => $notice_slug,
]
);
?>
<a href="<?php echo esc_url( $dismiss_url ); ?>" class="notice-dismiss">
<span class="screen-reader-text"><?php esc_html_e( 'Dismiss', 'wp-graphql' ); ?></span>
</a>
<?php } ?>
</div>
<?php
++$count;
}
}

/**
* Checks if the current admin page is within the scope of the plugin's own pages.
*
* @return bool True if the current page is within scope of the plugin's pages.
*/
protected function is_plugin_scoped_page(): bool {
$screen = get_current_screen();

// Guard clause for invalid screen.
if ( ! $screen ) {
return false;
}

$allowed_pages = [
'plugins',
'toplevel_page_graphiql-ide',
'graphql_page_graphql-settings',
];

$current_page_id = $screen->id;

return in_array( $current_page_id, $allowed_pages, true );
}

/**
* Checks if an admin notice should be displayed for WPGraphqlQL for ACF
*/
protected function should_display_acf_notice(): bool {
if ( ! class_exists( 'ACF' ) ) {
return false;
}

// Bail if new version of WPGraphQL for ACF is active.
if ( class_exists( 'WPGraphQLAcf' ) ) {
return false;
}

return true;
}

/**
* Handles the dismissal of the ACF notice.
* set_transient reference: https://developer.wordpress.org/reference/functions/set_transient/
* This function sets a transient to remember the dismissal status of the notice.
*/
public function handle_dismissal_of_acf_notice(): void {
if ( ! isset( $_GET['wpgraphql_disable_acf_notice_nonce'], $_GET['wpgraphql_disable_notice'] ) ) {
return;
}

$nonce = sanitize_text_field( wp_unslash( $_GET['wpgraphql_disable_acf_notice_nonce'] ) );
$notice_slug = sanitize_text_field( wp_unslash( $_GET['wpgraphql_disable_notice'] ) );

if ( empty( $notice_slug ) || ! wp_verify_nonce( $nonce, 'wpgraphql_disable_acf_notice_nonce' ) ) {
return;
}

$disabled = get_option( 'wpgraphql_dismissed_admin_notices', [] );
$disabled[] = $notice_slug;
update_option( 'wpgraphql_dismissed_admin_notices', array_unique( $disabled ) );

// Redirect to clear URL parameters
wp_safe_redirect( remove_query_arg( [ 'wpgraphql_disable_acf_notice_nonce', 'wpgraphql_disable_notice' ] ) );
exit();
}
}

0 comments on commit 2d1bc8b

Please sign in to comment.