579 lines
19 KiB
PHP
579 lines
19 KiB
PHP
<?php
|
||
/**
|
||
* Settings page for admin.
|
||
*
|
||
* @package ROBOTSTXT_SMTP_Newsletter
|
||
*/
|
||
|
||
namespace Robotstxt_SMTP_Newsletter\Admin;
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Settings page class.
|
||
*
|
||
* Manages plugin configuration in WordPress admin.
|
||
*/
|
||
class Settings_Page {
|
||
|
||
/**
|
||
* Singleton instance.
|
||
*
|
||
* @var Settings_Page|null
|
||
*/
|
||
private static $instance = null;
|
||
|
||
/**
|
||
* Option name.
|
||
*
|
||
* @var string
|
||
*/
|
||
private const OPTION_NAME = 'robotstxt_smtp_newsletter_options';
|
||
|
||
/**
|
||
* Get singleton instance.
|
||
*
|
||
* @return Settings_Page
|
||
*/
|
||
public static function get_instance() {
|
||
if ( null === self::$instance ) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* Constructor.
|
||
*/
|
||
private function __construct() {
|
||
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
||
// Note: We don't add admin_menu here because NewsletterMailerAddon parent handles menu creation.
|
||
// The parent expects admin/index.php to render the settings page.
|
||
}
|
||
|
||
/**
|
||
* Static method to register settings without creating instance.
|
||
* Used when parent addon handles menu creation.
|
||
*
|
||
* @return void
|
||
*/
|
||
public static function register_settings_static() {
|
||
// Just ensure the instance is created so register_settings hook is added.
|
||
self::get_instance();
|
||
}
|
||
|
||
/**
|
||
* Register settings.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function register_settings() {
|
||
register_setting(
|
||
'robotstxt_smtp_newsletter',
|
||
self::OPTION_NAME,
|
||
array(
|
||
'sanitize_callback' => array( $this, 'sanitize_options' ),
|
||
)
|
||
);
|
||
|
||
add_settings_section(
|
||
'robotstxt_smtp_newsletter_main',
|
||
__( 'SMTP (by ROBOTSTXT) Integration', 'robotstxt-smtp-newsletter' ),
|
||
array( $this, 'render_section' ),
|
||
'robotstxt_smtp_newsletter'
|
||
);
|
||
|
||
add_settings_field(
|
||
'enabled',
|
||
__( 'Enable Integration', 'robotstxt-smtp-newsletter' ),
|
||
array( $this, 'render_enabled_field' ),
|
||
'robotstxt_smtp_newsletter',
|
||
'robotstxt_smtp_newsletter_main'
|
||
);
|
||
|
||
add_settings_field(
|
||
'batch_size',
|
||
__( 'Batch Size', 'robotstxt-smtp-newsletter' ),
|
||
array( $this, 'render_batch_size_field' ),
|
||
'robotstxt_smtp_newsletter',
|
||
'robotstxt_smtp_newsletter_main'
|
||
);
|
||
|
||
add_settings_field(
|
||
'max_per_connection',
|
||
__( 'Max Emails Per Connection', 'robotstxt-smtp-newsletter' ),
|
||
array( $this, 'render_max_per_connection_field' ),
|
||
'robotstxt_smtp_newsletter',
|
||
'robotstxt_smtp_newsletter_main'
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Add settings page to admin menu.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function add_settings_page() {
|
||
add_submenu_page(
|
||
'newsletter_main_index',
|
||
__( 'SMTP (by ROBOTSTXT)', 'robotstxt-smtp-newsletter' ),
|
||
__( 'SMTP (by ROBOTSTXT)', 'robotstxt-smtp-newsletter' ),
|
||
'manage_options',
|
||
'robotstxt-smtp-newsletter',
|
||
array( $this, 'render_page' )
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Render settings page.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function render_page() {
|
||
if ( ! current_user_can( 'manage_options' ) ) {
|
||
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'robotstxt-smtp-newsletter' ) );
|
||
}
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||
|
||
<form action="options.php" method="post">
|
||
<?php
|
||
settings_fields( 'robotstxt_smtp_newsletter' );
|
||
do_settings_sections( 'robotstxt_smtp_newsletter' );
|
||
submit_button();
|
||
?>
|
||
</form>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Render section description.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function render_section() {
|
||
echo '<p>';
|
||
esc_html_e( 'Configure how Newsletter uses SMTP (by ROBOTSTXT) for email delivery.', 'robotstxt-smtp-newsletter' );
|
||
echo '</p>';
|
||
|
||
// Show current SMTP configuration.
|
||
if ( ! class_exists( '\Robotstxt_SMTP\Plugin' ) ) {
|
||
echo '<div class="notice notice-error inline"><p>';
|
||
esc_html_e( 'ROBOTSTXT SMTP plugin is not active. Please activate it first.', 'robotstxt-smtp-newsletter' );
|
||
echo '</p></div>';
|
||
return;
|
||
}
|
||
|
||
$plugin = \Robotstxt_SMTP_Newsletter\Plugin::get_instance();
|
||
$settings = $plugin->get_smtp_settings();
|
||
|
||
echo '<div class="notice notice-info inline"><p>';
|
||
echo '<strong>' . esc_html__( 'Current SMTP Configuration:', 'robotstxt-smtp-newsletter' ) . '</strong><br>';
|
||
echo '<ul style="margin: 10px 0 0 20px;">';
|
||
echo '<li>' . sprintf(
|
||
esc_html__( 'Host: %s:%d', 'robotstxt-smtp-newsletter' ),
|
||
esc_html( $settings['host'] ?? '' ),
|
||
absint( $settings['port'] ?? 0 )
|
||
) . '</li>';
|
||
|
||
$encryption = $settings['encryption'] ?? '';
|
||
if ( ! empty( $encryption ) ) {
|
||
echo '<li>' . sprintf(
|
||
esc_html__( 'Encryption: %s', 'robotstxt-smtp-newsletter' ),
|
||
esc_html( strtoupper( $encryption ) )
|
||
) . '</li>';
|
||
}
|
||
|
||
if ( \Robotstxt_SMTP\Plugin::is_amazon_ses_integration_active() ) {
|
||
echo '<li><strong>' . esc_html__( 'Amazon SES: Active', 'robotstxt-smtp-newsletter' ) . '</strong></li>';
|
||
}
|
||
|
||
echo '</ul>';
|
||
echo '</p></div>';
|
||
|
||
// Show optimal batch size recommendations.
|
||
$this->render_recommendations();
|
||
}
|
||
|
||
/**
|
||
* Render settings recommendations.
|
||
*
|
||
* @return void
|
||
*/
|
||
private function render_recommendations() {
|
||
$optimal = $this->calculate_optimal_settings();
|
||
$current_options = \Robotstxt_SMTP_Newsletter\Plugin::get_instance()->get_settings();
|
||
$current_batch = absint( $current_options['batch_size'] ?? 10 );
|
||
$current_max_conn = absint( $current_options['max_per_connection'] ?? 50 );
|
||
|
||
// Determine batch size status.
|
||
$batch_status = 'optimal';
|
||
if ( $current_batch < $optimal['batch_size'] * 0.5 ) {
|
||
$batch_status = 'too_low';
|
||
} elseif ( $current_batch > $optimal['batch_size'] * 1.5 ) {
|
||
$batch_status = 'too_high';
|
||
} elseif ( abs( $current_batch - $optimal['batch_size'] ) > 5 ) {
|
||
$batch_status = 'suboptimal';
|
||
}
|
||
|
||
// Determine max_per_connection status (only for SMTP).
|
||
$max_conn_status = 'optimal';
|
||
if ( ! $optimal['is_ses'] && $optimal['max_per_connection'] > 0 ) {
|
||
if ( $current_max_conn < $optimal['max_per_connection'] * 0.5 ) {
|
||
$max_conn_status = 'too_low';
|
||
} elseif ( $current_max_conn > $optimal['max_per_connection'] * 1.5 ) {
|
||
$max_conn_status = 'too_high';
|
||
} elseif ( abs( $current_max_conn - $optimal['max_per_connection'] ) > 20 ) {
|
||
$max_conn_status = 'suboptimal';
|
||
}
|
||
}
|
||
|
||
// Overall status.
|
||
$overall_status = 'optimal';
|
||
if ( 'too_low' === $batch_status || 'too_high' === $batch_status || 'too_low' === $max_conn_status || 'too_high' === $max_conn_status ) {
|
||
$overall_status = 'warning';
|
||
} elseif ( 'suboptimal' === $batch_status || 'suboptimal' === $max_conn_status ) {
|
||
$overall_status = 'info';
|
||
}
|
||
|
||
// Status styling.
|
||
$notice_class = 'notice-info';
|
||
$status_icon = 'ⓘ'; // Info icon.
|
||
|
||
if ( 'optimal' === $overall_status ) {
|
||
$notice_class = 'notice-success';
|
||
$status_icon = '✔'; // Checkmark.
|
||
} elseif ( 'warning' === $overall_status ) {
|
||
$notice_class = 'notice-warning';
|
||
$status_icon = '⚠'; // Warning icon.
|
||
}
|
||
|
||
echo '<div class="notice ' . esc_attr( $notice_class ) . ' inline" style="margin-top: 15px;"><p>';
|
||
echo '<strong>' . $status_icon . ' ' . esc_html__( 'Settings Recommendations', 'robotstxt-smtp-newsletter' ) . '</strong><br>';
|
||
echo '<ul style="margin: 10px 0 0 20px;">';
|
||
|
||
// Show cron interval.
|
||
echo '<li>' . sprintf(
|
||
/* translators: %d: seconds */
|
||
esc_html__( 'Newsletter cron runs every: %d seconds', 'robotstxt-smtp-newsletter' ),
|
||
$optimal['cron_interval']
|
||
) . '</li>';
|
||
|
||
// Show delivery method.
|
||
$delivery_method = $optimal['is_ses']
|
||
? __( 'Amazon SES API (REST)', 'robotstxt-smtp-newsletter' )
|
||
: __( 'SMTP with SMTPKeepAlive', 'robotstxt-smtp-newsletter' );
|
||
echo '<li>' . sprintf(
|
||
/* translators: %s: delivery method */
|
||
esc_html__( 'Delivery method: %s', 'robotstxt-smtp-newsletter' ),
|
||
esc_html( $delivery_method )
|
||
) . '</li>';
|
||
|
||
// Show calculation reasoning.
|
||
if ( ! empty( $optimal['reasoning'] ) ) {
|
||
foreach ( $optimal['reasoning'] as $reason ) {
|
||
echo '<li>' . esc_html( $reason ) . '</li>';
|
||
}
|
||
}
|
||
|
||
echo '</ul>';
|
||
|
||
// Show recommendations.
|
||
echo '<p style="margin-top: 10px;"><strong>';
|
||
|
||
// Batch size recommendation.
|
||
if ( 'optimal' === $batch_status ) {
|
||
echo '✔ ' . sprintf(
|
||
/* translators: %d: batch size */
|
||
esc_html__( 'Batch size: Your current setting (%d) is optimal!', 'robotstxt-smtp-newsletter' ),
|
||
$current_batch
|
||
);
|
||
} else {
|
||
echo '► ' . sprintf(
|
||
/* translators: %d: recommended batch size */
|
||
esc_html__( 'Recommended batch size: %d emails/batch', 'robotstxt-smtp-newsletter' ),
|
||
$optimal['batch_size']
|
||
);
|
||
|
||
if ( 'too_low' === $batch_status ) {
|
||
echo '<br> ' . sprintf(
|
||
/* translators: %d: current batch size */
|
||
esc_html__( 'Current (%d) is significantly lower than optimal. Increase it to send faster.', 'robotstxt-smtp-newsletter' ),
|
||
$current_batch
|
||
);
|
||
} elseif ( 'too_high' === $batch_status ) {
|
||
echo '<br> ' . sprintf(
|
||
/* translators: %d: current batch size */
|
||
esc_html__( 'Current (%d) is too high and may trigger rate limits. Reduce it to avoid errors.', 'robotstxt-smtp-newsletter' ),
|
||
$current_batch
|
||
);
|
||
} elseif ( 'suboptimal' === $batch_status ) {
|
||
echo '<br> ' . sprintf(
|
||
/* translators: 1: current batch size, 2: recommended batch size */
|
||
esc_html__( 'Current (%1$d) could be optimized. Consider changing to %2$d.', 'robotstxt-smtp-newsletter' ),
|
||
$current_batch,
|
||
$optimal['batch_size']
|
||
);
|
||
}
|
||
}
|
||
|
||
// Max per connection recommendation (only for SMTP).
|
||
if ( ! $optimal['is_ses'] && $optimal['max_per_connection'] > 0 ) {
|
||
echo '<br>';
|
||
if ( 'optimal' === $max_conn_status ) {
|
||
echo '✔ ' . sprintf(
|
||
/* translators: %d: max per connection */
|
||
esc_html__( 'Max per connection: Your current setting (%d) is optimal!', 'robotstxt-smtp-newsletter' ),
|
||
$current_max_conn
|
||
);
|
||
} else {
|
||
echo '► ' . sprintf(
|
||
/* translators: %d: recommended max per connection */
|
||
esc_html__( 'Recommended max per connection: %d emails', 'robotstxt-smtp-newsletter' ),
|
||
$optimal['max_per_connection']
|
||
);
|
||
|
||
if ( 'too_low' === $max_conn_status ) {
|
||
echo '<br> ' . sprintf(
|
||
/* translators: %d: current max per connection */
|
||
esc_html__( 'Current (%d) is too low. Increase to reduce connection overhead.', 'robotstxt-smtp-newsletter' ),
|
||
$current_max_conn
|
||
);
|
||
} elseif ( 'too_high' === $max_conn_status ) {
|
||
echo '<br> ' . sprintf(
|
||
/* translators: %d: current max per connection */
|
||
esc_html__( 'Current (%d) is too high and may cause SMTP timeouts. Reduce it.', 'robotstxt-smtp-newsletter' ),
|
||
$current_max_conn
|
||
);
|
||
} elseif ( 'suboptimal' === $max_conn_status ) {
|
||
echo '<br> ' . sprintf(
|
||
/* translators: 1: current max per connection, 2: recommended max per connection */
|
||
esc_html__( 'Current (%1$d) could be optimized. Consider changing to %2$d.', 'robotstxt-smtp-newsletter' ),
|
||
$current_max_conn,
|
||
$optimal['max_per_connection']
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
echo '</strong></p>';
|
||
|
||
echo '</p></div>';
|
||
}
|
||
|
||
/**
|
||
* Render enabled field.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function render_enabled_field() {
|
||
$options = get_option( self::OPTION_NAME, array() );
|
||
$enabled = ! empty( $options['enabled'] );
|
||
?>
|
||
<label>
|
||
<input type="checkbox" name="<?php echo esc_attr( self::OPTION_NAME ); ?>[enabled]" value="1" <?php checked( $enabled ); ?>>
|
||
<?php esc_html_e( 'Use SMTP (by ROBOTSTXT) for Newsletter delivery', 'robotstxt-smtp-newsletter' ); ?>
|
||
</label>
|
||
<p class="description">
|
||
<?php esc_html_e( 'When enabled, Newsletter will use SMTP (by ROBOTSTXT) configuration with SMTPKeepAlive for efficient bulk sending.', 'robotstxt-smtp-newsletter' ); ?>
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Render batch size field.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function render_batch_size_field() {
|
||
$options = get_option( self::OPTION_NAME, array() );
|
||
$batch_size = $options['batch_size'] ?? 10;
|
||
?>
|
||
<input type="number" name="<?php echo esc_attr( self::OPTION_NAME ); ?>[batch_size]" value="<?php echo esc_attr( $batch_size ); ?>" min="1" max="10000" class="small-text">
|
||
<p class="description">
|
||
<?php esc_html_e( 'Number of emails to send in each batch. Higher values improve performance with SMTPKeepAlive.', 'robotstxt-smtp-newsletter' ); ?><br>
|
||
<?php esc_html_e( 'See calculated recommendations below based on your rate limits and cron interval.', 'robotstxt-smtp-newsletter' ); ?>
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Render max per connection field.
|
||
*
|
||
* @return void
|
||
*/
|
||
public function render_max_per_connection_field() {
|
||
$options = get_option( self::OPTION_NAME, array() );
|
||
$max_per_connection = $options['max_per_connection'] ?? 50;
|
||
|
||
// Hide for Amazon SES.
|
||
if ( class_exists( '\Robotstxt_SMTP\Plugin' ) && \Robotstxt_SMTP\Plugin::is_amazon_ses_integration_active() ) {
|
||
?>
|
||
<p class="description">
|
||
<?php esc_html_e( 'Not applicable for Amazon SES (uses API instead of SMTP connections).', 'robotstxt-smtp-newsletter' ); ?>
|
||
</p>
|
||
<input type="hidden" name="<?php echo esc_attr( self::OPTION_NAME ); ?>[max_per_connection]" value="<?php echo esc_attr( $max_per_connection ); ?>">
|
||
<?php
|
||
return;
|
||
}
|
||
?>
|
||
<input type="number" name="<?php echo esc_attr( self::OPTION_NAME ); ?>[max_per_connection]" value="<?php echo esc_attr( $max_per_connection ); ?>" min="1" max="1000" class="small-text">
|
||
<p class="description">
|
||
<?php esc_html_e( 'Maximum emails to send before reconnecting to SMTP server. Prevents timeouts.', 'robotstxt-smtp-newsletter' ); ?><br>
|
||
<?php esc_html_e( 'See calculated recommendations below based on your rate limits.', 'robotstxt-smtp-newsletter' ); ?>
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Calculate optimal batch size and max per connection based on rate limits and cron interval.
|
||
*
|
||
* @return array<string, mixed> Array with 'batch_size', 'max_per_connection', 'reasoning', and 'status'.
|
||
*/
|
||
private function calculate_optimal_settings() {
|
||
$plugin = \Robotstxt_SMTP_Newsletter\Plugin::get_instance();
|
||
$settings = $plugin->get_smtp_settings();
|
||
|
||
// Get Newsletter cron interval (default 300 seconds).
|
||
$cron_interval = defined( 'NEWSLETTER_CRON_INTERVAL' ) ? NEWSLETTER_CRON_INTERVAL : 300;
|
||
|
||
// Get rate limits.
|
||
$rate_per_second = absint( $settings['rate_limit_per_second'] ?? 0 );
|
||
$rate_per_hour = absint( $settings['rate_limit_per_hour'] ?? 0 );
|
||
$rate_per_day = absint( $settings['rate_limit_per_day'] ?? 0 );
|
||
|
||
// Check if Amazon SES is active.
|
||
$is_ses = \Robotstxt_SMTP\Plugin::is_amazon_ses_integration_active();
|
||
|
||
$optimal_batch = 10; // Default fallback.
|
||
$optimal_max_conn = 50; // Default fallback.
|
||
$reasoning = array();
|
||
$limiting_factor = '';
|
||
|
||
// If no rate limits are set.
|
||
if ( 0 === $rate_per_second && 0 === $rate_per_hour && 0 === $rate_per_day ) {
|
||
$optimal_batch = $is_ses ? 50 : 20;
|
||
$optimal_max_conn = $is_ses ? 0 : 100; // SES doesn't need max per connection.
|
||
$reasoning[] = __( 'No rate limits configured.', 'robotstxt-smtp-newsletter' );
|
||
$reasoning[] = $is_ses
|
||
? __( 'Using default for Amazon SES (50 emails/batch).', 'robotstxt-smtp-newsletter' )
|
||
: __( 'Using default for SMTP (20 emails/batch, 100 max per connection).', 'robotstxt-smtp-newsletter' );
|
||
$limiting_factor = 'default';
|
||
} else {
|
||
// Calculate based on rate limits.
|
||
$candidates = array();
|
||
|
||
// Per-second limit: Calculate how many emails we can send in one cron cycle.
|
||
if ( $rate_per_second > 0 ) {
|
||
// Reserve time for connection overhead (10s for SMTP, 2s for SES).
|
||
$overhead = $is_ses ? 2 : 10;
|
||
$available_time = max( 1, $cron_interval - $overhead );
|
||
$max_emails = floor( $rate_per_second * $available_time );
|
||
$candidates['per_second'] = $max_emails;
|
||
$reasoning[] = sprintf(
|
||
/* translators: 1: emails per second, 2: cron interval, 3: overhead, 4: calculated max */
|
||
__( 'Per-second limit: %1$d emails/sec × (%2$d sec - %3$d sec overhead) = %4$d emails/batch', 'robotstxt-smtp-newsletter' ),
|
||
$rate_per_second,
|
||
$cron_interval,
|
||
$overhead,
|
||
$max_emails
|
||
);
|
||
}
|
||
|
||
// Per-hour limit: Divide by number of cron executions per hour.
|
||
if ( $rate_per_hour > 0 ) {
|
||
$batches_per_hour = floor( 3600 / $cron_interval );
|
||
$max_emails = floor( $rate_per_hour / $batches_per_hour );
|
||
$candidates['per_hour'] = $max_emails;
|
||
$reasoning[] = sprintf(
|
||
/* translators: 1: emails per hour, 2: batches per hour, 3: calculated max */
|
||
__( 'Per-hour limit: %1$d emails/hour ÷ %2$d batches/hour = %3$d emails/batch', 'robotstxt-smtp-newsletter' ),
|
||
$rate_per_hour,
|
||
$batches_per_hour,
|
||
$max_emails
|
||
);
|
||
}
|
||
|
||
// Per-day limit: Divide by number of cron executions per day.
|
||
if ( $rate_per_day > 0 ) {
|
||
$batches_per_day = floor( 86400 / $cron_interval );
|
||
$max_emails = floor( $rate_per_day / $batches_per_day );
|
||
$candidates['per_day'] = $max_emails;
|
||
$reasoning[] = sprintf(
|
||
/* translators: 1: emails per day, 2: batches per day, 3: calculated max */
|
||
__( 'Per-day limit: %1$d emails/day ÷ %2$d batches/day = %3$d emails/batch', 'robotstxt-smtp-newsletter' ),
|
||
$rate_per_day,
|
||
$batches_per_day,
|
||
$max_emails
|
||
);
|
||
}
|
||
|
||
// The optimal batch size is the minimum of all candidates.
|
||
if ( ! empty( $candidates ) ) {
|
||
$optimal_batch = min( $candidates );
|
||
$limiting_factor = array_search( $optimal_batch, $candidates, true );
|
||
|
||
// Apply safety margin (90%).
|
||
$optimal_batch = max( 1, floor( $optimal_batch * 0.9 ) );
|
||
|
||
$reasoning[] = sprintf(
|
||
/* translators: 1: limiting factor, 2: final batch size */
|
||
__( 'Most restrictive limit: %1$s. Recommended batch size (with 10%% safety margin): %2$d', 'robotstxt-smtp-newsletter' ),
|
||
$limiting_factor,
|
||
$optimal_batch
|
||
);
|
||
}
|
||
|
||
// Calculate optimal max_per_connection for SMTP (not needed for SES).
|
||
if ( ! $is_ses ) {
|
||
// Max per connection should be 2-5x the batch size, or based on hourly limits.
|
||
if ( $rate_per_hour > 0 ) {
|
||
// Conservative: use 10 minutes of the hourly limit.
|
||
$optimal_max_conn = floor( ( $rate_per_hour / 6 ) * 0.9 );
|
||
} else {
|
||
$optimal_max_conn = min( 500, max( 50, $optimal_batch * 3 ) );
|
||
}
|
||
$reasoning[] = sprintf(
|
||
/* translators: %d: max per connection */
|
||
__( 'Max per connection (SMTP): %d emails before reconnecting to prevent timeouts.', 'robotstxt-smtp-newsletter' ),
|
||
$optimal_max_conn
|
||
);
|
||
}
|
||
}
|
||
|
||
return array(
|
||
'batch_size' => $optimal_batch,
|
||
'max_per_connection' => $optimal_max_conn,
|
||
'reasoning' => $reasoning,
|
||
'limiting_factor' => $limiting_factor,
|
||
'cron_interval' => $cron_interval,
|
||
'is_ses' => $is_ses,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Sanitize options.
|
||
*
|
||
* @param array<string, mixed> $options Raw options.
|
||
*
|
||
* @return array<string, mixed> Sanitized options.
|
||
*/
|
||
public function sanitize_options( $options ) {
|
||
$clean = array();
|
||
|
||
$clean['enabled'] = ! empty( $options['enabled'] );
|
||
|
||
// Batch size: Allow up to 10,000 for high-throughput scenarios (SES, high rate limits).
|
||
$clean['batch_size'] = max( 1, min( 10000, (int) ( $options['batch_size'] ?? 10 ) ) );
|
||
|
||
// Max per connection: Allow up to 1,000 for providers that support it.
|
||
$clean['max_per_connection'] = max( 1, min( 1000, (int) ( $options['max_per_connection'] ?? 50 ) ) );
|
||
|
||
return $clean;
|
||
}
|
||
}
|