diff --git a/public/app/themes/clarity/functions.php b/public/app/themes/clarity/functions.php index f4456ddf7..f5e2bed2a 100644 --- a/public/app/themes/clarity/functions.php +++ b/public/app/themes/clarity/functions.php @@ -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'; diff --git a/public/app/themes/clarity/inc/admin/plugins/co-authors-plus.php b/public/app/themes/clarity/inc/admin/plugins/co-authors-plus.php index 9c225e43c..55ceddc73 100644 --- a/public/app/themes/clarity/inc/admin/plugins/co-authors-plus.php +++ b/public/app/themes/clarity/inc/admin/plugins/co-authors-plus.php @@ -1,5 +1,7 @@ $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; } /** @@ -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]); } } } @@ -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; } @@ -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); + } } diff --git a/public/app/themes/clarity/inc/admin/transient-admin-notices.php b/public/app/themes/clarity/inc/admin/transient-admin-notices.php new file mode 100644 index 000000000..1f1aff357 --- /dev/null +++ b/public/app/themes/clarity/inc/admin/transient-admin-notices.php @@ -0,0 +1,186 @@ +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( + '
%s
', + 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());