Skip to content

Instantly share code, notes, and snippets.

@gschoppe
Forked from obenland/gist:5173811
Last active October 16, 2022 16:46
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gschoppe/dd7d12577974166529ea0139f1f53939 to your computer and use it in GitHub Desktop.
Save gschoppe/dd7d12577974166529ea0139f1f53939 to your computer and use it in GitHub Desktop.
Implements a standardized messaging system that allows admin notices to be passed across page redirects. usage: add_filter( 'wp_persistent_notices', function( $notices ) { $notices[] = array( 'type' => 'error', 'message' => "Houston, we have a problem!" ); return $notices; } );
<?php if( !defined( 'ABSPATH' ) ) { die(); } // Include in all php files, to prevent direct execution
/**
* Class Name : WP Persistent Notices
* Description : Implements a Standardized messaging system that allows admin messages to be passed across page redirects.
* Class URI : http://gschoppe.com/wordpress/pass-wordpress-admin-notices-across-page-redirects/
* Version : 1.0.0
* Author : Greg Schoppe
* Author URI : http://gschoppe.com
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
**/
if( !class_exists( 'WP_Persistent_Notices' ) ) {
class WP_Persistent_Notices { // replace ClassName with the name of your class
private static $_this;
private $session_var = 'wp_persistent_notices';
private $max_age;
private $default_notice;
private $notices;
public static function Instance() {
static $instance = null;
if ( $instance === null ) {
$instance = new self();
}
return $instance;
}
private function __construct() {
$this->max_age = max( intval( ini_get( 'max_execution_time' ) ) * 2, 30 );
$this->notices = array();
$this->default_notice = array(
'id' => '',
'type' => 'info',
'custom_class' => '',
'message' => '',
'dismissible' => false,
'html' => '',
'location' => 'default'
);
add_action( 'admin_init' , array( $this, 'admin_init' ) );
add_action( 'admin_notices' , array( $this, 'render_notices' ) );
add_action( 'wp_ajax_get_persistent_notices', array( $this, 'ajax_get_notices' ) );
add_action( 'shutdown' , array( $this, 'save_notices' ) );
}
public function admin_init() {
// add a filter and that returns true to replace or prevent the session initialization functionality
if( !apply_filters( 'wp_persistent_notices_replace_initialization', false ) ) {
session_start();
}
}
public function add_notice( $notice = array() ) {
if( !is_array( $notice ) ) {
$notice = array(
'message' => $notice
);
}
$notice = array_merge( $this->default_notice, $notice );
$notice['created'] = time();
$this->notices[] = $notice;
return true;
}
public function render_notices( $location = 'default' ) {
if( !$location ) {
$location = 'default';
}
if( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
$notices = $this->retrieve_notices( $location );
$output_array = array();
foreach ( $notices as $notice ) {
if( !is_array( $notice ) ) {
$notice = array(
'message' => $notice
);
}
if( $notice['html'] ) {
$output_array[] = wp_kses_post( $notice['html'] );
} elseif( $notice['message'] ) {
$notice = array_merge( $this->default_notice, $notice );
$id = '';
if( $notice['id'] ) {
$id = ' id="wp-persistent-notice-' . sanitize_title( $id ) . '"';
}
$type = '';
if( $notice['type'] ) {
$type = ' notice-' . sanitize_title( $notice['type'] );
}
$cclass = '';
if( $notice['custom_class'] ) {
$cclass = ' ' . sanitize_title( $notice['custom_class'] );
}
$dismissible = '';
if( $notice['dismissible'] ) {
$dismissible = ' is-dismissible';
}
$class = ' class="notice' . $type . $cclass . $dismissible . '"';
$message = wp_kses_post( $notice['message'] );
$output_array[] = sprintf( '<div%1$s%2$s><p>%3$s</p></div>', $id, $class, $message );
}
}
echo implode( "\n", $output_array );
}
public function ajax_get_notices() {
$location = 'default';
if( isset( $_GET['location'] ) && $_GET['location'] ) {
$location = $_GET['location'];
}
if( isset( $_POST['location'] ) && $_POST['location'] ) {
$location = $_POST['location'];
}
$notices = $this->retrieve_notices( $location );
echo json_encode( $notices );
wp_die();
}
public function save_notices() {
$notices = $this->retrieve_notices( 'all' );
if ( isset( $notices ) ) {
// add a filter that returns true to replace the notice saving functionality
if( apply_filters( 'wp_persistent_notices_replace_save_notices', false, $notices ) !== true ) {
$_SESSION[ $this->session_var ] = $notices;
}
}
}
private function retrieve_notices( $location = 'default' ) {
// note: once a notice is retrieved, it's gone
$notices = array();
// add a filter that returns true to prevent retrieving messages on specific pages
if( !apply_filters( 'wp_persistent_notices_prevent_retrieval', false ) ) {
// add a filter that returns notices or empty array() to replace the session retrieval functionality
$raw_notices = apply_filters( 'wp_persistent_notices_replace_retrieve_notices', false );
if( $raw_notices === false ) {
$raw_notices = array();
if( isset( $_SESSION[ $this->session_var ] ) && is_array( $_SESSION[ $this->session_var ] ) ) {
$session_notices = $_SESSION[ $this->session_var ];
if( is_array( $session_notices ) ) {
$raw_notices = $session_notices;
}
}
}
$raw_notices += $this->notices;
$raw_notices = apply_filters( 'wp_persistent_notices', $raw_notices );
// clear all places notices are stored
unset( $_SESSION[ $this->session_var ] );
$this->notices = array();
remove_all_filters( 'wp_persistent_notices' );
$notices = array();
// filter for age and location
foreach( $raw_notices as $notice ) {
if( !isset( $notice['created'] ) || !$notice['created'] ) {
$notice['created'] = time();
} else {
if( time() - $notice['created'] > $this->max_age ) {
continue;
}
}
if( $location == 'all' || $notice['location'] == $location ) {
$notices[] = $notice;
} else {
$this->notices[] = $notice;
}
}
// attach a filter that returns true to disable grouping notices
if( !apply_filters( 'wp_persistent_notices_display_ungrouped', false ) ) {
usort( $notices, array( $this, 'compare_notices' ) );
}
}
return $notices;
}
private function compare_notices( $a, $b ) {
// attach a filter that returns an integer { -1, 0, 1 } to replace comparison function
$compare = apply_filters( 'wp_persistent_notices_replace_comparison', false, $a, $b );
if( $compare !== false ) {
return $compare;
}
// if one of them doesn't have a type
if( !isset( $a['type'] ) ) {
if( !isset( $b['type'] ) ) {
return 0;
}
return -1;
}
// if types are equal
if( $a['type'] == $b['type'] ) {
return 0;
}
// give a weight to each type
$test = array( $a['type'], $b['type'] );
$results = array();
foreach( $test as $val ) {
$i = false;
switch( $val ) {
case 'error':
$i = apply_filters( 'wp_persistent_notices_sort_error_weight', 0 );
break;
case 'warning':
$i = apply_filters( 'wp_persistent_notices_sort_warning_weight', 5 );
break;
case 'success':
$i = apply_filters( 'wp_persistent_notices_sort_success_weight', 10 );
break;
case 'info':
$i = apply_filters( 'wp_persistent_notices_sort_info_weight', 15 );
break;
default:
// use the val to determine weight
$i = apply_filters( 'wp_persistent_notices_sort_custom_weight', 20, $val );
}
$results[] = $i;
}
return ( $results[0] - $results[1] );
}
}
WP_Persistent_Notices::Instance();
if( !function_exists( 'add_persistent_notice' ) ) {
function add_persistent_notice( $notice ) {
$cl = WP_Persistent_Notices::Instance();
return $cl->add_notice( $notice );
}
}
if( !function_exists( 'render_persistent_notices' ) ) {
function render_persistent_notices( $location = 'default' ) {
$cl = WP_Persistent_Notices::Instance();
return $cl->render_notices( $location );
}
}
}
@dameer
Copy link

dameer commented Feb 25, 2017

Owh my God, finally something that really works! Been struggling with persistent notices problem in WordPress for the last couple of days until I found your class. This should really be a part of WP core.
Zillions of THANKS my friend, you saved me!

@luiseduardobraschi
Copy link

I'd just add a verification for sessions already started:

if( session_status() == PHP_SESSION_NONE ) session_start();

Then it would be perfect!

@samcroft
Copy link

Thanks for sharing this, it's exactly what I was looking for! Really surprised that they don't include something like this out of the box.

@szepeviktor
Copy link

szepeviktor commented Nov 4, 2019

Really surprised that they don't include something like this out of the box.

Please consider this package [spam link here]
Its code is very simple, has many features and uses transients.

@gschoppe
Copy link
Author

gschoppe commented Nov 4, 2019

Its code is very simple, has many features and uses transients.

your code does not solve one of the core issues with WordPress messages, which is the need for transactional messages that can survive over a double redirect. Your code targets notices to classes of user or all users, but not to the specific user who initiated an action. In multi-user environments, this means that one user might cause an action, and another might receive the notification.

Beyond that, it is considered very rude to spam pages for one solution to advertise your alternative.

@szepeviktor
Copy link

Excuse me.

@szepeviktor
Copy link

You're right. My code is not for general purpose WordPress usage.
It is for system usage when some central thing goes wrong: e.g. "Google API is not reachable" in wp-cron, or "All editors, please prepare for release!"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment