How can I display an admin notification?
I want to do some actions on the save_post_post action and want to inform the admin about the success and details about the action after that.
I tried so far:
add_action('save_post_post', "do_Stuff");
function do_stuff( $postId ){
session_start();
//do stuff and create some $msg;
set_transient( 'temporary_message', $msg, 60*60*12 );
add_action( 'admin_notices', 'show_notice');
}
function show_notice(){
?>
<div class="notice notice-error">
<p>Fehler: <?=get_transient( 'temporary_message' )?></p>
</div>
<?php
}
The Thing is, this notification is not shown, because the browser does not reload after saving, because the saving happens via ajax ... my save_post_post hook is called.
I already tried to create and output some JSON, as I get an error-admin-notification if I output any text, that my JSON is invalid. But When I send JSON and inspect the network traffic in Chrome, I don't see any response and .. the earlier error is still there.
How can I show an Admin Notification anyways?
How can I display an admin notification?
I want to do some actions on the save_post_post action and want to inform the admin about the success and details about the action after that.
I tried so far:
add_action('save_post_post', "do_Stuff");
function do_stuff( $postId ){
session_start();
//do stuff and create some $msg;
set_transient( 'temporary_message', $msg, 60*60*12 );
add_action( 'admin_notices', 'show_notice');
}
function show_notice(){
?>
<div class="notice notice-error">
<p>Fehler: <?=get_transient( 'temporary_message' )?></p>
</div>
<?php
}
The Thing is, this notification is not shown, because the browser does not reload after saving, because the saving happens via ajax ... my save_post_post hook is called.
I already tried to create and output some JSON, as I get an error-admin-notification if I output any text, that my JSON is invalid. But When I send JSON and inspect the network traffic in Chrome, I don't see any response and .. the earlier error is still there.
How can I show an Admin Notification anyways?
Share Improve this question edited Apr 1, 2020 at 15:45 helle asked Apr 1, 2020 at 15:15 hellehelle 3181 gold badge4 silver badges18 bronze badges 5 |1 Answer
Reset to default 3Presenting Notices
Gutenberg provides a mechanism for displaying notices in the form of the Notices Data package. In Gutenberg's editors, the package's selectors and action creators are exposed on the core/notices
store, and can be accessed by any standard means therein, e.g.:
useDispatch( 'core/notices' )
hook in functional components (including theedit()
function of a block type, and therender()
function of a block editor plugin registered using@wordpress/plugins
'registerPlugin()
function).withDispatch()
HOC factory for class-based components.dispatch( 'core/notices' )
for any other code.
Functional Component Example
import { useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
const MyComponent = props => {
const [ error, setError ] = useState( null );
if( error ) {
useDispatch( 'core/notices' ).createNotice( {
status: 'error',
content: error,
} );
setError( null );
}
// ...
}
Subscribing to a Save Operation
I am not certain that this is the best mechanism for accomplishing this - and whether or not it is appropriate for your code really depends on your specific use-case - but in general we often implement the concept of "listening for events" in Gutenberg and Redux by instead subscribing to application state updates and testing for specific changes.
By that logic, one way to execute code when a save operation completes is to observe the change in return value from the isPostSaving()
selector on the core/editor
store:
import { useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
const MyComponent = props => {
const is_saving = useSelect( select => select( 'core/editor' ).isSavingPost() );
const [ was_saving, setWasSaving ] = useState( is_saving );
if( was_saving ) {
// If state transitioned from saving -> not saving, execute post-save logic.
if( ! is_saving ) {
setWasSaving( false );
console.log( 'Post Save Finished' );
}
}
else if( is_saving ) {
// If state transitioned from not saving -> saving, set was_saving.
setWasSaving( true );
}
// ...
}
We could also extract that logic to a custom hook in order to make it more portable:
const usePostSaved = () => {
const is_saving = useSelect( select => select( 'core/editor' ).isSavingPost() );
const [ was_saving, setWasSaving ] = useState( is_saving );
if( was_saving ) {
if( ! is_saving ) {
setWasSaving( false );
return true;
}
}
else if( is_saving ) {
setWasSaving( true );
}
return false;
}
const MyComponent = props => {
const saved = usePostSaved();
if( saved )
console.log( 'Post Save Finished' );
// ...
}
Acquiring Notices From the Back-End
If you have back-end code generating notices, one solution for displaying them in the editor would be to queue them in transients, then expose those transients for an asynchronous request from your JavaScript by implementing a custom REST API endpoint or a wp_ajax_
handler. In either scenario, the @wordpress/api-fetch
package can help you succinctly perform the network request.
It might also be possible to leverage the Heartbeat API to this end, or otherwise expose the notices in a manner which could be accessed via Gutenberg's built-in selectors (like adding them to post meta, for example).
Example: AJAX Handler/Block Editor Plugin
A save action hook and AJAX handler to provide the notice might look like this:
function wpse36297_handle_post_save( $post_id, $post, $update ) {
// ...
if( $a_critical_error ) {
$notice = [
'status' => 'error',
'message' => __( 'Things done blew up :/', 'wpse36297' ),
'isDismissable' => true,
];
}
else {
$notice = [
'status' => 'success',
'message' => __( 'This was a triumph! \\o/', 'wpse36297' ),
'isDismissable' => true,
];
}
set_transient( 'wpse36297-notice-post_' . $post_id, $notice, 60*60 );
}
add_action( 'save_post_post', 'wpse36297_handle_post_save', 10, 3 );
function wpse36297_ajax_get_post_notice() {
$post_id = (int) $_GET['post_id'];
if( ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error(
[
'msg' => __( 'User is not permitted to edit this post.', 'wpse36297' ),
],
403
);
}
$notice = get_transient( 'wpse36297-notice-post_' . $post_id );
if( $notice )
delete_transient( 'wpse36297-notice-post_' . $post_id );
wp_send_json_success( $notice );
}
add_action( 'wp_ajax_wpse36297-get-post-notice', 'wpse36297_ajax_get_post_notice' );
We can then make a request to that handler once state updates indicate that a save has just completed.
Aside: the code below uses
wp.ajax.settings.url
which is provided by thewp-util
script. If you are using@wordpress/scripts
/@wordpress/create-block
, this script will not be automatically added to your script dependencies in the.asset.php
file as it is not a Gutenberg package.
import apiFetch from '@wordpress/api-fetch';
import { registerPlugin } from '@wordpress/plugins';
import { useState, useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
const fetchPostNotice = async ( post_id ) => {
const url = new URL( window.location.origin + wp.ajax.settings.url );
url.searchParams.append( 'action', 'wpse36297-get-post-notice' );
url.searchParams.append( 'post_id', post_id );
const res = await apiFetch( { url: url.toString() } );
if( ! res.success )
return { status: 'error', message: res.data.msg };
return res.data;
};
registerPlugin(
'wpse362975',
{
render: () => {
const { createNotice } = useDispatch( 'core/notices' );
const { getCurrentPostId } = useSelect( 'core/editor' );
const is_saving = useSelect( select => select( 'core/editor' ).isSavingPost() );
const [ was_saving, setWasSaving ] = useState( is_saving );
useEffect(
async () => {
if( was_saving && ! is_saving ) {
setWasSaving( false );
const notice = await fetchPostNotice( getCurrentPostId() );
if( ! notice )
return;
const { status, message, ...options } = notice;
createNotice( status, message, options );
}
else if( is_saving && ! was_saving ) {
setWasSaving( true );
}
},
[ is_saving, was_saving ]
);
return null;
}
}
);
save_post
and almost every other hook associated with publishing or updating run twice. So it might be wiser to try to change some of the built-in Editor text than try to show an admin notice on any hook. – WebElaine Commented Apr 1, 2020 at 21:02