371 lines
9.8 KiB
PHP
371 lines
9.8 KiB
PHP
<?php
|
|
/**
|
|
* Main plugin class.
|
|
*
|
|
* @package ROBOTSTXT_SMTP_Newsletter
|
|
*/
|
|
|
|
namespace Robotstxt_SMTP_Newsletter;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Plugin main class.
|
|
*
|
|
* Extends NewsletterMailerAddon to integrate with Newsletter plugin.
|
|
*/
|
|
class Plugin extends \NewsletterMailerAddon {
|
|
|
|
/**
|
|
* Singleton instance.
|
|
*
|
|
* @var Plugin|null
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Default options.
|
|
*
|
|
* @var array<string, mixed>
|
|
*/
|
|
private static $defaults = array(
|
|
'enabled' => false,
|
|
'batch_size' => 10,
|
|
);
|
|
|
|
/**
|
|
* Plugin display name.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $name;
|
|
|
|
/**
|
|
* Plugin slug.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $slug;
|
|
|
|
/**
|
|
* Menu title.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $menu_title;
|
|
|
|
/**
|
|
* Get singleton instance.
|
|
*
|
|
* @return Plugin
|
|
*/
|
|
public static function get_instance() {
|
|
if ( null === self::$instance ) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Initialize plugin (called on newsletter_loaded hook).
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function instance_init() {
|
|
$instance = self::get_instance();
|
|
$instance->init();
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
private function __construct() {
|
|
parent::__construct( 'robotstxt-smtp-newsletter', ROBOTSTXT_SMTP_NEWSLETTER_VERSION, ROBOTSTXT_SMTP_NEWSLETTER_PATH );
|
|
$this->name = 'SMTP (by ROBOTSTXT)';
|
|
$this->slug = 'robotstxt-smtp-newsletter';
|
|
$this->menu_title = 'SMTP (by ROBOTSTXT)';
|
|
}
|
|
|
|
/**
|
|
* Setup options.
|
|
*
|
|
* Overrides parent to use our custom option name.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setup_options() {
|
|
if ( $this->options ) {
|
|
return;
|
|
}
|
|
// Load our options using our custom option name (not newsletter_ prefix).
|
|
$this->options = get_option( 'robotstxt_smtp_newsletter_options', array() );
|
|
$this->options = wp_parse_args( $this->options, self::$defaults );
|
|
}
|
|
|
|
/**
|
|
* Initialize plugin hooks.
|
|
*
|
|
* Overrides parent init() method.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function init() {
|
|
parent::init();
|
|
|
|
// Load required classes.
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-queue.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-statistics.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-email-message.php';
|
|
|
|
// Initialize database tables.
|
|
Queue::get_instance();
|
|
Statistics::get_instance();
|
|
|
|
// Load text domain.
|
|
add_action( 'init', array( $this, 'load_textdomain' ) );
|
|
|
|
// Force batch size in Newsletter cron.
|
|
add_filter( 'newsletter_max_emails', array( $this, 'filter_max_emails' ), 999 );
|
|
|
|
// Register admin pages.
|
|
if ( is_admin() ) {
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'admin/class-settings-page.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'admin/class-emails-list-page.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'admin/class-statistics-page.php';
|
|
|
|
add_action( 'admin_init', array( 'Robotstxt_SMTP_Newsletter\Admin\Settings_Page', 'register_settings_static' ) );
|
|
add_action( 'admin_menu', array( 'Robotstxt_SMTP_Newsletter\Admin\Emails_List_Page', 'register' ) );
|
|
add_action( 'admin_menu', array( 'Robotstxt_SMTP_Newsletter\Admin\Statistics_Page', 'register' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filter Newsletter's max emails per execution.
|
|
*
|
|
* Forces Newsletter to use our configured batch_size.
|
|
*
|
|
* @param int $max_emails Newsletter's calculated max emails.
|
|
*
|
|
* @return int Our configured batch_size.
|
|
*/
|
|
public function filter_max_emails( $max_emails ) {
|
|
// Only apply if our addon is enabled.
|
|
$settings = $this->get_settings();
|
|
if ( empty( $settings['enabled'] ) ) {
|
|
return $max_emails;
|
|
}
|
|
|
|
return absint( $settings['batch_size'] ?? 10 );
|
|
}
|
|
|
|
/**
|
|
* Load plugin text domain for translations.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function load_textdomain() {
|
|
load_plugin_textdomain(
|
|
'robotstxt-smtp-newsletter',
|
|
false,
|
|
dirname( plugin_basename( ROBOTSTXT_SMTP_NEWSLETTER_FILE ) ) . '/languages'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the mailer instance.
|
|
*
|
|
* This method is called by the parent class to get the mailer
|
|
* that should be registered with Newsletter.
|
|
*
|
|
* @return \NewsletterMailer
|
|
*/
|
|
public function get_mailer() {
|
|
// Load required classes.
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-queue.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-statistics.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-email-message.php';
|
|
|
|
// Get our settings and map to Newsletter's expected format.
|
|
$settings = $this->get_settings();
|
|
$smtp_settings = $this->get_smtp_settings();
|
|
|
|
// Calculate optimal speed (emails per hour) based on rate limits.
|
|
$speed = $this->calculate_optimal_speed( $smtp_settings );
|
|
|
|
// CRITICAL: For queue-based architecture, speed must be high enough
|
|
// that Newsletter loads all emails at once. Newsletter calculates:
|
|
// batch_size = speed / (3600 / NEWSLETTER_CRON_INTERVAL)
|
|
// To get batch_size = configured value, we need:
|
|
// speed = batch_size * (3600 / NEWSLETTER_CRON_INTERVAL)
|
|
$batch_size = $settings['batch_size'] ?? 10;
|
|
|
|
// Ensure NEWSLETTER_CRON_INTERVAL is loaded
|
|
if ( ! defined( 'NEWSLETTER_CRON_INTERVAL' ) ) {
|
|
$cron_file = WP_PLUGIN_DIR . '/newsletter/includes/cron.php';
|
|
if ( file_exists( $cron_file ) ) {
|
|
require_once $cron_file;
|
|
}
|
|
}
|
|
|
|
// Calculate runs per hour based on Newsletter's cron interval
|
|
$cron_interval = defined( 'NEWSLETTER_CRON_INTERVAL' ) ? NEWSLETTER_CRON_INTERVAL : 300;
|
|
$runs_per_hour = (int) ( 3600 / $cron_interval );
|
|
$calculated_speed = max( $speed, $batch_size * $runs_per_hour );
|
|
|
|
// Newsletter expects 'turbo' for batch_size and 'speed' for emails/hour.
|
|
$mailer_options = array(
|
|
'turbo' => $batch_size,
|
|
'speed' => $calculated_speed,
|
|
);
|
|
|
|
// Detect if Amazon SES is active.
|
|
if ( $this->is_amazon_ses_active() ) {
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-amazonses-mailer.php';
|
|
return new AmazonSES_Mailer( $mailer_options );
|
|
} else {
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-smtp-mailer.php';
|
|
return new SMTP_Mailer( $mailer_options );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get maximum emails per execution.
|
|
*
|
|
* Newsletter checks this method to determine batch size.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_max_emails() {
|
|
$settings = $this->get_settings();
|
|
return absint( $settings['batch_size'] ?? 10 );
|
|
}
|
|
|
|
/**
|
|
* Get batch size.
|
|
*
|
|
* Alternative method that Newsletter may check.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_batch() {
|
|
return $this->get_max_emails();
|
|
}
|
|
|
|
/**
|
|
* Override Newsletter's calculation of emails per run.
|
|
*
|
|
* Newsletter normally calculates: batch = speed / runs_per_hour
|
|
* For queue-based architecture, we want to enqueue all emails at once.
|
|
*
|
|
* @return int Configured batch_size.
|
|
*/
|
|
public function get_emails_per_run() {
|
|
$settings = $this->get_settings();
|
|
if ( empty( $settings['enabled'] ) ) {
|
|
// If addon is disabled, use parent's calculation.
|
|
return parent::get_emails_per_run();
|
|
}
|
|
|
|
return absint( $settings['batch_size'] ?? 10 );
|
|
}
|
|
|
|
/**
|
|
* Calculate optimal speed (emails per hour) based on rate limits.
|
|
*
|
|
* @param array<string, mixed> $smtp_settings SMTP settings with rate limits.
|
|
*
|
|
* @return int Emails per hour.
|
|
*/
|
|
private function calculate_optimal_speed( $smtp_settings ) {
|
|
// Get rate limits from ROBOTSTXT SMTP settings.
|
|
$rate_per_hour = absint( $smtp_settings['rate_limit_per_hour'] ?? 0 );
|
|
|
|
// If hourly limit is set, use it (with 90% safety margin).
|
|
if ( $rate_per_hour > 0 ) {
|
|
return (int) floor( $rate_per_hour * 0.9 );
|
|
}
|
|
|
|
// If no hourly limit, calculate from per-second limit.
|
|
$rate_per_second = absint( $smtp_settings['rate_limit_per_second'] ?? 0 );
|
|
if ( $rate_per_second > 0 ) {
|
|
return (int) floor( $rate_per_second * 3600 * 0.9 );
|
|
}
|
|
|
|
// If no limits set, use a conservative default based on delivery method.
|
|
$is_ses = $this->is_amazon_ses_active();
|
|
return $is_ses ? 1000 : 500;
|
|
}
|
|
|
|
/**
|
|
* Check if Amazon SES integration is active.
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function is_amazon_ses_active() {
|
|
if ( ! class_exists( '\Robotstxt_SMTP\Plugin' ) ) {
|
|
return false;
|
|
}
|
|
|
|
return \Robotstxt_SMTP\Plugin::is_amazon_ses_integration_active();
|
|
}
|
|
|
|
/**
|
|
* Get plugin settings with defaults.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function get_settings() {
|
|
$options = get_option( 'robotstxt_smtp_newsletter_options', array() );
|
|
|
|
return wp_parse_args( $options, self::$defaults );
|
|
}
|
|
|
|
/**
|
|
* Get SMTP settings from robotstxt-smtp plugin.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function get_smtp_settings() {
|
|
if ( ! class_exists( '\Robotstxt_SMTP\Plugin' ) || ! class_exists( '\Robotstxt_SMTP\Admin\Settings_Page' ) ) {
|
|
return array();
|
|
}
|
|
|
|
// Check if network mode is enabled.
|
|
$scope = get_site_option( 'robotstxt_smtp_configuration_scope', 'site' );
|
|
|
|
if ( 'network' === $scope ) {
|
|
$settings = get_site_option( 'robotstxt_smtp_network_options', array() );
|
|
} else {
|
|
$settings = get_option( 'robotstxt_smtp_options', array() );
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Plugin activation hook.
|
|
*
|
|
* Creates database tables and migrates settings.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function activation_hook() {
|
|
// Load required classes.
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-queue.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-statistics.php';
|
|
require_once ROBOTSTXT_SMTP_NEWSLETTER_PATH . 'includes/class-email-message.php';
|
|
|
|
// Tables are created automatically via __construct().
|
|
Queue::get_instance();
|
|
Statistics::get_instance();
|
|
|
|
// Migrate settings: remove deprecated max_per_connection.
|
|
$options = get_option( 'robotstxt_smtp_newsletter_options', array() );
|
|
if ( isset( $options['max_per_connection'] ) ) {
|
|
unset( $options['max_per_connection'] );
|
|
update_option( 'robotstxt_smtp_newsletter_options', $options );
|
|
}
|
|
}
|
|
}
|