Flash notices

After handling an admin form, the usual pattern is post/redirect/get: process the submission, redirect, and tell the user what happened on the page they land on. A notice can't simply be echoed, because the redirect discards the output. Flash notices are stored until the user's next admin page:

add_action( 'admin_post_acme_import', function () use ( $plugin ) {
	check_admin_referer( 'acme_import' );

	$result = Importer::run();

	if ( $result->ok() ) {
		$plugin->notices()->flash( 'success', __( 'Import finished.', 'acme' ) );
	} else {
		$plugin->notices()->flash( 'error', __( 'Some rows were skipped:', 'acme' ), $result->errors() );
	}

	wp_safe_redirect( wp_get_referer() ?: admin_url() );
	exit;
} );

flash( $type, $message, $items = [] ):

Argument
$type success, error, warning or info (anything else becomes info).
$message Plain text. It's escaped when printed, so don't pass HTML.
$items Optional details, printed as a bulleted list under the message. Each is plain text, or a link (see below).

It returns false, and stores nothing, when no user is logged in.

Items are plain text and escaped like the message, so HTML in them shows as text. To link to something, such as the item you just created, pass the item as an array with text and url:

$plugin->notices()->flash(
	'success',
	__( 'Link created:', 'acme' ),
	array(
		array(
			'text' => sprintf( '%s → %s', $post->post_title, $shortUrl ),
			'url'  => get_edit_post_link( $post->ID, 'raw' ),
		),
	)
);

It's printed as <a href="…">…</a>, with the text escaped by esc_html() and the URL by esc_url(), so neither can inject markup. If url is missing or empty, or esc_url() rejects it (a javascript: URL, for example), the text is printed without a link. Plain-text and link items can be mixed in one list.

How it works

  • Notices are stored per user, in a transient named {prefix}_notices_{user id}, and expire after 10 minutes if they're never shown. Several flashes before a redirect all appear.
  • They're printed once, as dismissible WordPress notices, on the next admin screen that user loads (including network and user admin screens), then deleted.
  • Each plugin has its own store, so plugins built on the framework never print each other's notices.

$plugin->notices()->pending() returns the queued notices without printing them, which is useful in tests.