Stop WordPress From Sending Emails (And Log Them Too!)

While doing some development work recently, I wanted to make sure that I disabled all email sending in my test site so that no users imported would get any weird emails.

To do this I had ChatGPT whip me up a quick plugin, and after cleaning it up here it is to share with the world:

<?php
/**
 * Plugin Name: Restrict and Log Emails
 * Description: Blocks emails for users and logs all email attempts.
 * Version: 1.3
 * Author: Emrikol
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Prevent direct access.
}
define( 'REL_EMAIL_LOG_DB_VERSION', '1.2' );

/**
 * Database installation and upgrade function.
 * Creates or updates the email_log table if needed based on a stored version option.
 *
 * @return void
 */
function rel_install() {
	global $wpdb;
	$table_name      = $wpdb->prefix . 'email_log';
	$charset_collate = $wpdb->get_charset_collate();

	$sql = "CREATE TABLE $table_name (
	   id mediumint(9) NOT NULL AUTO_INCREMENT,
	   recipient_email varchar(100) NOT NULL,
	   subject text NOT NULL,
	   message text NOT NULL,
	   status varchar(20) NOT NULL,
	   sent_at datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
	   PRIMARY KEY (id)
	) $charset_collate;";

	require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	dbDelta( $sql );

	// Update the database version option.
	update_option( 'rel_email_log_db_version', REL_EMAIL_LOG_DB_VERSION, false );
}
register_activation_hook( __FILE__, 'rel_install' );

/**
 * Adds the Email Log submenu to Tools in the WordPress admin area.
 *
 * @return void
 */
function rel_add_admin_menu(): void {
	if ( function_exists( 'add_submenu_page' ) ) {
		add_submenu_page(
			'tools.php',
			'Email Log',
			'Email Log',
			'manage_options',
			'email-log',
			'rel_email_log_page'
		);
	}
}
add_action( 'admin_menu', 'rel_add_admin_menu' );

/**
 * Displays the Email Log page in the WordPress admin area.
 *
 * @return void
 */
function rel_email_log_page(): void {
	global $wpdb;
	$table_name = $wpdb->prefix . 'email_log';
	$logs       = $wpdb->get_results( "SELECT * FROM $table_name ORDER BY sent_at DESC LIMIT 50" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared

	echo '<div class="wrap">';
	echo '<h1>Email Log</h1>';

	if ( current_user_can( 'manage_options' ) ) {
		// Process toggle form submission.
		if ( isset( $_POST['rel_toggle_blocking'] ) && check_admin_referer( 'rel_toggle_blocking_action', 'rel_toggle_blocking_nonce' ) ) {
			// Sanitize and update the blocking option.
			if ( isset( $_POST['rel_email_blocking_enabled'] ) && in_array( $_POST['rel_email_blocking_enabled'], array( 'enabled', 'disabled' ), true ) ) {
				$blocking = $_POST['rel_email_blocking_enabled']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			} else {
				$blocking = 'enabled';
			}

			update_option( 'rel_email_blocking_enabled', $blocking, false );
			echo '<div class="updated notice"><p>Email blocking has been ' . ( 'enabled' === $blocking ? 'enabled' : 'disabled' ) . '.</p></div>';
		}
		$current_blocking = get_option( 'rel_email_blocking_enabled', true );

		echo '<form method="post" style="margin-bottom:20px;">';
		wp_nonce_field( 'rel_toggle_blocking_action', 'rel_toggle_blocking_nonce' );
		echo '<p>Email blocking is currently <strong>' . ( 'enabled' === $current_blocking ? 'enabled' : 'disabled' ) . '</strong>.';
		echo '<br/><br/><label for="rel_email_blocking_enabled">Toggle: </label>';
		echo '<select id="rel_email_blocking_enabled" name="rel_email_blocking_enabled">';
		echo '<option value="enabled"' . selected( $current_blocking, 'enabled', false ) . '>Enabled</option>';
		echo '<option value="disabled"' . selected( $current_blocking, 'disabled', false ) . '>Disabled</option>';
		echo '</select> ';
		echo '<input type="submit" name="rel_toggle_blocking" value="Update" class="button-primary" />';
		echo '</p>';
		echo '</form>';
	}

	echo '<table class="widefat fixed" cellspacing="0">';
	echo '<thead><tr><th>ID</th><th>Email</th><th>Subject</th><th>Message</th><th>Status</th><th>Sent At</th></tr></thead>';
	echo '<tbody>';

	if ( $logs ) {
		foreach ( $logs as $log ) {
			$truncated_message = ( strlen( $log->message ) > 30 ) ? substr( $log->message, 0, 30 ) . '…' : $log->message;
			echo wp_kses_post(
				'<tr>
					<td>' . $log->id . '</td>
					<td>' . $log->recipient_email . '</td>
					<td>' . $log->subject . '</td>
					<td>' . $truncated_message . '</td>
					<td>' . $log->status . '</td>
					<td>' . $log->sent_at . '</td>
				  </tr>'
			);
		}
	} else {
		echo '<tr><td colspan="6">No emails logged yet.</td></tr>';
	}

	echo '</tbody></table>';
	echo '</div>';
}

/**
 * Intercepts email sending attempts, restricts emails for users without the 'manage_options' capability, and logs the attempt.
 *
 * @param null|bool $short_circuit Default value if email is allowed.
 * @param array     $atts An array of email attributes (to, subject, etc.).
 * @return bool|null Returns a non-null value to short-circuit email sending if restricted.
 */
function rel_prevent_email( $short_circuit, $atts ) {
	global $wpdb;
	$table_name = $wpdb->prefix . 'email_log';

	$recipient_email = isset( $atts['to'] ) ? $atts['to'] : '';
	$subject         = isset( $atts['subject'] ) ? $atts['subject'] : '';
	$message         = isset( $atts['message'] ) ? $atts['message'] : '';
	$sent_at         = current_time( 'mysql' );

	// Check if email blocking is enabled (default true).
	$blocking = get_option( 'rel_email_blocking_enabled', 'enabled' );
	switch ( $blocking ) {
		case 'enabled':
			$blocking_enabled = true;
			break;
		case 'disabled':
			$blocking_enabled = false;
			break;
		default:
			$blocking_enabled = true;
			break;
	}

	// Determine status based on blocking setting.
	if ( true === $blocking_enabled ) {
		$status = 'Blocked';
	} else {
		$status = 'Sent';
	}

	// Log the email attempt.
	$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$table_name,
		array(
			'recipient_email' => $recipient_email,
			'subject'         => $subject,
			'message'         => $message,
			'status'          => $status,
			'sent_at'         => $sent_at,
		)
	);

	// If blocking is enabled and the current user cannot manage options, block the email, otherwise allow sending.
	if ( $blocking_enabled ) {
		return true;
	}
	return null;
}
add_filter( 'pre_wp_mail', 'rel_prevent_email', 10, 2 );

/**
 * Checks the current database version and updates the email_log table if necessary.
 *
 * @return void
 */
function rel_check_db_version() {
	if ( get_option( 'rel_email_log_db_version' ) !== REL_EMAIL_LOG_DB_VERSION ) {
		rel_install();
	}
}
add_action( 'init', 'rel_check_db_version' );

Code language: PHP (php)

As usual, don’t use this slop, nor anything you read here because this is my blarg and I can’t be trusted to do anything correct.

Other Posts Not Worth Reading

Hey, You!

Like this kind of garbage? Subscribe for more! I post like once a month or so, unless I found something interesting to write about.


Comments

Leave a Reply