Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login and Registration: Add expiration support for dismissing notices. #95

Open
wants to merge 4 commits into
base: trunk
Choose a base branch
from
Open
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
45 changes: 40 additions & 5 deletions src/js/_enqueues/admin/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -1109,11 +1109,46 @@ $( function() {
$button.find( '.screen-reader-text' ).text( __( 'Dismiss this notice.' ) );
$button.on( 'click.wp-dismiss-notice', function( event ) {
event.preventDefault();
$el.fadeTo( 100, 0, function() {
$el.slideUp( 100, function() {
$el.remove();

var $dismiss_data = { action: 'dismiss-notice' },
$slug = $el.data( 'slug' ),
$expiration = $el.data( 'expiration' );

if ( ! $slug ) {
$el.fadeTo( 100, 0, function() {
$el.slideUp( 100, function() {
$el.remove();
});
});
});
} else {
$dismiss_data.slug = $slug;

if ( $expiration ) {
$dismiss_data.expiration = $expiration;
}

$.post(
ajaxurl,
$dismiss_data
).always( function ( response ) {
if ( true === response.success ) {
$el.fadeTo( 100, 0, function() {
$el.slideUp( 100, function() {
$el.remove();
});
});
} else {
var $noticeDismissalFailed = $( '#notice-dismissal-failed' );

if ( 0 === $noticeDismissalFailed.length ) {
$el.after( '<div id="notice-dismissal-failed" class="notice notice-error"><p>' + response.data + '</p></div>' );
} else {
$el.after( $noticeDismissalFailed );
$noticeDismissalFailed.find( 'p' ).innerHTML = response.data;
}
}
} );
}
});

$el.append( $button );
Expand Down Expand Up @@ -1733,7 +1768,7 @@ $( function() {
setTimeout( function() {
var focusIsInToggle = $.contains( toggleButton, focusedElement );
var focusIsInSidebar = $.contains( sidebar, focusedElement );

if ( ! focusIsInToggle && ! focusIsInSidebar ) {
$( toggleButton ).trigger( 'click.wp-responsive' );
}
Expand Down
1 change: 1 addition & 0 deletions src/wp-admin/admin-ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
'health-check-get-sizes',
'toggle-auto-updates',
'send-password-reset',
'dismiss-notice',
);

// Deprecated.
Expand Down
37 changes: 37 additions & 0 deletions src/wp-admin/includes/ajax-actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5610,3 +5610,40 @@ function wp_ajax_send_password_reset() {
wp_send_json_error( $results->get_error_message() );
}
}

/**
* Handles dismissing a notice via AJAX.
*
* @since 6.5.0
*/
function wp_ajax_dismiss_notice() {
check_ajax_referer( 'dismiss-notice', 'nonce' );

if ( ! isset( $_POST['slug'] ) ) {
wp_send_json_error( __( 'Failed to dismiss notice: The notice does not have a slug.' ) );
}

$slug = trim( sanitize_text_field( $_POST['slug'] ) );
if ( '' === $slug ) {
wp_send_json_error( __( "Failed to dismiss notice: The notice's slug must not be an empty string." ) );
}

$expiration = 0;
if ( isset( $_POST['expiration'] ) ) {
if ( ! is_numeric( $_POST['expiration'] ) ) {
wp_send_json_error( __( 'Failed to dismiss notice: The expiration time must be a number of seconds.' ) );
}

$expiration = (int) $_POST['expiration'];

if ( 0 > $_POST['expiration'] ) {
wp_send_json_error( __( 'Failed to dismiss notice: The expiration time must be greater than or equal to 0.' ) );
}
}

if ( false === set_site_transient( "wp_admin_notice_dismissed_{$slug}", 1, $expiration ) ) {
wp_send_json_error( __( 'Failed to dismiss notice: The notice could not be dismissed.' ) );
}

wp_send_json_success( __( 'The notice was successfully dismissed.' ) );
}
126 changes: 118 additions & 8 deletions src/wp-includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -8756,14 +8756,24 @@ function wp_fuzzy_number_match( $expected, $actual, $precision = 1 ) {
* @param array $args {
* Optional. An array of arguments for the admin notice. Default empty array.
*
* @type string $type Optional. The type of admin notice.
* For example, 'error', 'success', 'warning', 'info'.
* Default empty string.
* @type bool $dismissible Optional. Whether the admin notice is dismissible. Default false.
* @type string $id Optional. The value of the admin notice's ID attribute. Default empty string.
* @type string[] $additional_classes Optional. A string array of class names. Default empty array.
* @type string[] $attributes Optional. Additional attributes for the notice div. Default empty array.
* @type bool $paragraph_wrap Optional. Whether to wrap the message in paragraph tags. Default true.
* @type string $type Optional. The type of admin notice.
* For example, 'error', 'success', 'warning', 'info'.
* Default empty string.
* @type bool|array $dismissible {
* Optional. Whether the admin notice is dismissible. Default false.
*
* If false, the notice is not dismissible.
* If true, the notice is dismissible until the next page load.
* If an array, the notice will be dismissed either permanently,
* or until the specified expiration has occurred.
*
* @type string $slug The slug of the notice. Used to store a transient when the notice is dismissed.
* @type int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
* }
* @type string $id Optional. The value of the admin notice's ID attribute. Default empty string.
* @type string[] $additional_classes Optional. A string array of class names. Default empty array.
* @type string[] $attributes Optional. Additional attributes for the notice div. Default empty array.
* @type bool $paragraph_wrap Optional. Whether to wrap the message in paragraph tags. Default true.
* }
* @return string The markup for an admin notice.
*/
Expand Down Expand Up @@ -8822,6 +8832,106 @@ function wp_get_admin_notice( $message, $args = array() ) {

if ( true === $args['dismissible'] ) {
$classes .= ' is-dismissible';
} elseif ( is_array( $args['dismissible'] ) ) {
// The "slug" key is required.
if ( ! isset( $args['dismissible']['slug'] ) || ! is_string( $args['dismissible']['slug'] ) ) {
wp_trigger_error(
__FUNCTION__,
sprintf(
/* translators: 1: The "slug" key, 2: The "dismissible" key. */
__( 'The "%1$s" key in the "%2$s" array must be a string.' ),
'slug',
'dismissible'
)
);
} else {
$slug = trim( $args['dismissible']['slug'] );
if ( '' === $slug ) {
wp_trigger_error(
__FUNCTION__,
sprintf(
/* translators: 1: The "slug" key, 2: The "dismissible" key. */
__( 'The "%1$s" key in the "%2$s" array must be a non-empty string.' ),
'slug',
'dismissible'
)
);
} else {
// Add a slug data attribute so a transient can be saved when the notice is dismissed.
$args['attributes']['data-slug'] = $slug;
}

/*
* If the notice is still dismissed, return early with
* empty notice markup so that nothing can be output.
*/
if ( 1 === (int) get_site_transient( "wp_admin_notice_dismissed_{$slug}" ) ) {
return '';
}

/*
* The "expiration" key is optional.
*
* `isset()` is not used because it will not catch `null` values, which are invalid.
*/
if ( array_key_exists( 'expiration', $args['dismissible'] ) ) {
if ( ! is_int( $args['dismissible']['expiration'] ) ) {
/*
* Unset the slug data attribute so that the notice appears on the next page load,
* allowing for corrections to be made without needing to delete the notice's transient.
*
* Without this, the notice would be permanently dismissed.
*/
unset( $args['attributes']['data-slug'] );

wp_trigger_error(
__FUNCTION__,
sprintf(
/* translators: 1: The "expiration" key, 2: The "dismissible" key. */
__( 'The "%1$s" key in the "%2$s" array must be an integer.' ),
'expiration',
'dismissible'
)
);
} else {
$expiration = (int) $args['dismissible']['expiration'];
if ( 0 > $expiration ) {
/*
* Unset the slug data attribute so that the notice appears on the next page load,
* allowing for corrections to be made without needing to delete the notice's transient.
*
* Without this, the notice would be permanently dismissed.
*/
unset( $args['attributes']['data-slug'] );

wp_trigger_error(
__FUNCTION__,
sprintf(
/* translators: 1: The "expiration" key, 2: The "dismissible" key. */
__( 'The "%1$s" key in the "%2$s" array must be greater than or equal to 0.' ),
'expiration',
'dismissible'
)
);
} else {
// Add an expiration data attribute so the notice can be dismissed for a specified duration.
$args['attributes']['data-expiration'] = $expiration;
}
}
}

/*
* By only adding the HTML class when everything is considered valid,
* this means invalid values will result in a notice that cannot be dismissed.
*
* Even if error reporting is disabled for some reason, the developer will know
* immediately that something has gone wrong.
*/
if ( isset( $args['attributes']['data-slug'] ) ) {
$args['attributues']['data-nonce'] = wp_create_nonce( 'dismiss-notice' );
$classes .= ' is-dismissible';
}
}
}

if ( is_array( $args['additional_classes'] ) && ! empty( $args['additional_classes'] ) ) {
Expand Down
1 change: 1 addition & 0 deletions tests/phpunit/includes/testcase-ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ abstract class WP_Ajax_UnitTestCase extends WP_UnitTestCase {
'get-post-thumbnail-html',
'wp-privacy-export-personal-data',
'wp-privacy-erase-personal-data',
'dismiss-notice',
);

public static function set_up_before_class() {
Expand Down
Loading