Skip to content

Commit

Permalink
CDPT-2013 Show co-author-plus errors as admin notices - instead of 50…
Browse files Browse the repository at this point in the history
…0.html (#746)

* CDPT-2013 Show co-autor-plus errors as admin notices - instead of 500.html

* Update functions.php

* CDPT-2013 Remove `WordPress` string from error message & edit screen.
  • Loading branch information
EarthlingDavey authored Oct 28, 2024
1 parent faadfcb commit 7f3a3b7
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 12 deletions.
1 change: 1 addition & 0 deletions public/app/themes/clarity/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
require_once 'inc/admin/remove-customizer.php';
require_once 'inc/admin/suppress-wp-update-msg.php';
require_once 'inc/admin/tinymce-editor-settings.php';
require_once 'inc/admin/transient-admin-notices.php';
require_once 'inc/admin/users/add-acf-capabilities.php';
require_once 'inc/admin/users/add-notes-from-antonia.php';
require_once 'inc/admin/users/remove-agency-admin-admin-access.php';
Expand Down
130 changes: 118 additions & 12 deletions public/app/themes/clarity/inc/admin/plugins/co-authors-plus.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use MOJ\Intranet\TransientAdminNotices;

/**
* Modifications to adapt the co-authors-plus plugin to our Clarity theme.
*
Expand Down Expand Up @@ -28,18 +30,18 @@ function custom_register_coauthors()

function custom_get_coauthors($object, $field_name, $request)
{
$coauthors = get_coauthors($object['id']);
$coauthors = get_coauthors($object['id']);

$authors = array();
$authors = array();
foreach ($coauthors as $author) {
$authors[] = array(
'display_name' => $author->display_name,
'author_id' => $author->ID,
'thumbnail_avatar' => get_the_post_thumbnail_url($author->ID, 'intranet-large'),
);
$authors[] = array(
'display_name' => $author->display_name,
'author_id' => $author->ID,
'thumbnail_avatar' => get_the_post_thumbnail_url($author->ID, 'intranet-large'),
);
};

return $authors;
return $authors;
}

/**
Expand All @@ -60,10 +62,10 @@ function dw_add_author_fields($fields_to_return, $groups)
];

foreach ($fields_to_return as $index => $field) {
$fields_to_delete = [ 'yim', 'aim', 'jabber', 'yahooim', 'website' ];
$fields_to_delete = ['yim', 'aim', 'jabber', 'yahooim', 'website'];

if (in_array($field['key'], $fields_to_delete)) {
unset($fields_to_return[ $index ]);
unset($fields_to_return[$index]);
}
}
}
Expand All @@ -80,10 +82,10 @@ function dw_add_author_fields($fields_to_return, $groups)

function dw_edit_contactmethods($contactmethods)
{
$fields_to_delete = [ 'yim', 'aim', 'jabber', 'yahooim', 'website' ];
$fields_to_delete = ['yim', 'aim', 'jabber', 'yahooim', 'website'];

foreach ($fields_to_delete as $field) {
unset($contactmethods[ $field ]);
unset($contactmethods[$field]);
}
return $contactmethods;
}
Expand Down Expand Up @@ -132,4 +134,108 @@ function check_local_avatar($url, $id_or_email, $args)
// always return an avatar
return $url ?: 'https://www.gravatar.com/avatar/?d=mp';
}

/**
* Filter wp_die_handler to use custom handler for when the post type is guest-author
*
* This is necessary because the co-authors plugin uses wp_die to handle errors
* and we need to override the default handler to understand what error happened.
* Without this, the error message is not displayed and the user will see the static 500.html page.
*
* @see https://github.com/Automattic/Co-Authors-Plus/issues/227 - Open issue to replace wp_die
* @see https://developer.wordpress.org/reference/hooks/wp_die_handler/ - wp_die_handler hook
*
* @param string $handler The current handler
* @return string The new handler
*/

add_filter('wp_die_handler', 'coauthors_filter_wp_die_handler');

function coauthors_filter_wp_die_handler(string $handler): string
{
global $post;

// If the post does not have an error and is a guest-author post type.
if (!is_wp_error($post) && $post->post_type === 'guest-author') {
return 'coauthors_wp_die_handler';
}

return $handler;
}


add_filter('gettext', 'coauthors_filter_text', 10, 3);

/**
* Filter the text of the plugin to remove the string 'WordPress'.
*
* @see https://developer.wordpress.org/reference/hooks/gettext/
*
* @param string $translated_text The translated text
* @param string $text The original text
* @param string $domain The text domain
* @return string The modified text
*/

function coauthors_filter_text(string $translated_text, string $text, string $domain): string
{
if ($domain === 'co-authors-plus') {
// Remove the string 'WordPress' from the plugin's text.
$translated_text = str_replace('WordPress user', 'user', $translated_text);
$translated_text = str_replace('WordPress User Mapping', 'User Mapping', $translated_text);
}

return $translated_text;
}

/**
* Custom handler for wp_die when the post type is guest-author
*
* This function will either:
* - redirect to the referer url and add an admin notice with the error message.
* - or, send the error message to Sentry and run the original wp_die handler.
*
* @param string $message The error message
* @param string $title The error title
* @param array $args Additional arguments
* @return void
*/

function coauthors_wp_die_handler(string $message, string $title = '', array $args = array()): void
{
global $post;

$user_id = get_current_user_id();

$expected_referer = "/wp/wp-admin/post.php?post={$post->ID}&action=edit";

// If a user is logged in , and the referer is the expected referer...
if ($user_id && $expected_referer === wp_get_referer() && class_exists('MOJ\Intranet\TransientAdminNotices')) {
// Create a new instance of the transient admin notices class
$notice_transient = new TransientAdminNotices('theme_user_notice:' . $user_id);

// Add the error to the notice queue.
$notice_transient->add($title, $message, 'error');

// Redirect to the referring page.
wp_safe_redirect($expected_referer);
die();
}

// Create a new WP_Error object with the error message.
// In coauthors_filter_wp_die_handler, is_wp_error($post) will return true.
// This is essential to prevent an infinite loop.
$post = new WP_Error($message);

// Send the error to Sentry.
if (is_plugin_active('wp-sentry/wp-sentry.php')) {
\Sentry\captureException(new Exception($message));
}

// Get the original die handler.
$die_handler = apply_filters('wp_die_handler', '_default_wp_die_handler');

// Call the original die handler.
call_user_func($die_handler, $message, $title, $args);
}
}
186 changes: 186 additions & 0 deletions public/app/themes/clarity/inc/admin/transient-admin-notices.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<?php

namespace MOJ\Intranet;

/**
* Class TransientAdminNotices
*
* Handles displaying admin notices in WordPress after a page redirect or reload.
*
* This is a vendored php package that's been copied from the original source
* and modified to the MOJ\Intranet namespace.
*
* @see https://github.com/wpscholar/wp-transient-admin-notices - Original source
*
* @author Micah Wood
* @package MOJ\Intranet
*/

class TransientAdminNotices implements \Countable, \IteratorAggregate {

const TYPES = [
'success',
'info',
'warning',
'error'
];

/**
* Queue of notices stored during the current page load
*
* @var array
*/
protected $notices = [];

/**
* Transient name
*
* @var string
*/
protected $transient;

/**
* TransientAdminNotices constructor.
*
* @param string $transient The name of the transient. Note: You may want to make this user-specific!
*/
public function __construct( $transient ) {
$this->transient = $transient;
add_action( 'admin_notices', array( $this, 'render' ) );
add_action( 'shutdown', array( $this, 'save' ) );
}

/**
* Add a new notice to the queue
*
* @param string $key
* @param string $message
* @param string $type
*/
public function add( $key, $message, $type = 'info' ) {
$this->notices[ $key ] = [
'message' => $message,
'type' => in_array( $type, self::TYPES ) ? $type : 'info',
];
}

/**
* Check if a notice exists in the queue
*
* @param string $key
*
* @return bool
*/
public function has( $key ) {
return array_key_exists( $key, $this->notices );
}

/**
* Get a notice from the queue
*
* @param string $key
*
* @return string|null
*/
public function get( $key ) {
return $this->has( $key ) ? $this->notices[ $key ] : null;
}

/**
* Remove a notice from the queue
*
* @param string $key
*/
public function remove( $key ) {
unset( $this->notices[ $key ] );
}

/**
* Purge all notices from queue
*/
public function purge() {
$this->notices = [];
}

/**
* Count number of notices in the queue
*/
public function count(): int {
return count( $this->notices );
}

/**
* Get array iterator for notices
*
* @return \ArrayIterator
*/
public function getIterator(): \ArrayIterator {
return new \ArrayIterator( $this->notices );
}

/**
* Set transient
*/
public function save() {
if ( $this->count() ) {
set_transient( $this->transient, $this->notices, 10 );
}
}

/**
* Get transient
*/
public function fetch() {
return get_transient( $this->transient );
}

/**
* Delete transient
*/
public function delete() {
delete_transient( $this->transient );
}

/**
* Render one or all notices from previous page load.
*
* @param string|null $key
*/
public function render( $key = null ) {

$notices = $this->fetch();

// We render all notices by default, unless a specific key is passed.
if ( $key ) {
$notices = isset( $notices, $notices[ $key ] ) ? $notices[ $key ] : [];
}

// Loop through and render all notices
if ( $notices && is_array( $notices ) ) {

$allowed_html = wp_kses_allowed_html();
$allowed_html['p'] = [];

foreach ( $notices as $notice ) {
if ( isset( $notice['message'], $notice['type'] ) ) {

printf(
'<div class="%s">%s</div>',
esc_attr( "notice notice-{$notice['type']}" ),
wp_kses( wpautop( $notice['message'] ), $allowed_html )
);

}
}

}

}

}

/**
* Init the TransientAdminNotices class
*/

new TransientAdminNotices('theme_user_notice:' . get_current_user_id());

0 comments on commit 7f3a3b7

Please sign in to comment.