Added helper classes for campaigns, email, and db

This commit is contained in:
Ruben Ramirez 2024-12-04 20:18:36 -06:00
parent cbe6834c03
commit c8002a4194
17 changed files with 3032 additions and 46 deletions

View file

@ -40,4 +40,11 @@ button.button-primary:hover {
table.rl_admin_meta_table {
width: 100%;
table-layout: fixed;
}
}
#campaign-timeline-heatmap {
width: 100%; /* Ensure it spans the available space */
height: 300px; /* Set a fixed height */
min-height: 300px; /* Ensure a minimum height */
background-color: #f9f9f9; /* Optional: for better visibility during debugging */
}

View file

@ -0,0 +1,410 @@
<?php
/**
* Helper class for managing Campaigns.
*/
class RL_MailWarmer_Campaign_Helper
{
/**
* Calculate the campaign timeline with randomized daily email goals.
*
* @param int $campaign_id The campaign post ID.
* @return array Weekly email schedule with randomized daily goals.
* @throws Exception If the campaign parameters are invalid.
*/
public static function calculate_campaign_timeline($campaign_id)
{
// Fetch campaign details
$warmup_period = (int) get_post_meta($campaign_id, 'warmup_period', true); // Weeks
$target_volume = (int) get_post_meta($campaign_id, 'target_volume', true); // Daily emails to ramp up to
$start_date = get_post_meta($campaign_id, 'start_date', true); // Campaign start date
if (!$warmup_period || !$target_volume || !$start_date) {
throw new Exception(__('Invalid campaign parameters.', 'rl-mailwarmer'));
}
// Fetch ACF options
$holidays_raw = get_field('calendar_holidays', 'option'); // Holidays in 12/25/25 format
$holidays = array_map(
fn($date) => DateTime::createFromFormat('m/d/y', trim($date))->format('Y-m-d'),
explode("\n", $holidays_raw)
);
$min_starting_volume = (int) get_field('min_starting_email_volume', 'option') ?: 10;
$max_daily_volume = (int) get_field('max_campaign_daily_email_volume', 'option') ?: 1000;
// Calculate starting daily volume (2.5% of target volume)
$starting_daily_volume = max(ceil($target_volume * 0.025), $min_starting_volume);
// Initialize variables
$timeline = [];
$total_days = $warmup_period * 7; // Total days in the campaign
$start_date = new DateTime($start_date);
// Calculate daily ramp-up rate
$daily_increase = ($target_volume - $starting_daily_volume) / $total_days;
// Generate timeline
for ($day = 0; $day < $total_days; $day++) {
$current_date = clone $start_date;
$current_date->modify("+{$day} days");
$date_formatted = $current_date->format('Y-m-d');
// Adjust for holidays and weekends
$is_weekend = in_array($current_date->format('N'), [6, 7]);
$is_holiday = in_array($date_formatted, $holidays);
$reduction_factor = ($is_weekend || $is_holiday) ? rand(65, 82) / 100 : 1;
// Calculate daily volume
$daily_volume = min(
ceil(($starting_daily_volume + ($daily_increase * $day)) * $reduction_factor),
$target_volume * 1.2
);
if ($daily_volume > $max_daily_volume) {
$daily_volume = $max_daily_volume;
}
$timeline[$date_formatted] = $daily_volume;
}
// Save the timeline as a JSON string to the campaign post
$timeline_json = json_encode($timeline);
// log_to_file("Timeline JSON: $timeline_json");
update_post_meta($campaign_id, 'campaign_timeline', $timeline_json);
return $timeline;
}
/**
* Generate a conversation for a campaign.
*
* @param int $campaign_id The ID of the campaign.
* @param array $conversation_steps Blueprint of the conversation (participants, replies, etc.).
* @param string $prompt AI prompt for generating email content.
*
* @return int|WP_Error The ID of the created conversation or a WP_Error on failure.
*/
public static function generate_conversation($campaign_id, $conversation_steps, $prompt) {
if (empty($campaign_id) || empty($conversation_steps)) {
return new WP_Error('invalid_data', __('Invalid campaign or conversation steps.', 'rl-mailwarmer'));
}
// Insert the conversation into the database
$conversation_id = RL_MailWarmer_DB_Helper::insert_conversation($campaign_id, $conversation_steps, $prompt);
if (!$conversation_id) {
return new WP_Error('db_error', __('Failed to create conversation.', 'rl-mailwarmer'));
}
return $conversation_id;
}
/**
* Generate messages for a conversation.
*
* @param int $conversation_id The ID of the conversation.
* @param int $campaign_id The ID of the campaign.
* @param array $steps The steps of the conversation.
*
* @return array An array of message IDs or WP_Error on failure.
*/
public static function generate_messages($conversation_id, $campaign_id, $steps) {
if (empty($conversation_id) || empty($campaign_id) || empty($steps)) {
return new WP_Error('invalid_data', __('Invalid conversation or steps.', 'rl-mailwarmer'));
}
$message_ids = [];
foreach ($steps as $step) {
$scheduled_for = $step['scheduled_for'];
$from_email = $step['from'];
$to_email = implode(',', $step['to']);
$cc = isset($step['cc']) ? implode(',', $step['cc']) : null;
$subject = $step['subject'];
$body = $step['body'];
// Insert the message into the database
$message_id = RL_MailWarmer_DB_Helper::insert_message(
$campaign_id,
$conversation_id,
$scheduled_for,
$from_email,
$to_email,
$cc,
$subject,
$body
);
if (!$message_id) {
return new WP_Error('db_error', __('Failed to create message.', 'rl-mailwarmer'));
}
$message_ids[] = $message_id;
}
return $message_ids;
}
/**
* Generate conversations and messages for a campaign.
*
* @param int $campaign_id The ID of the campaign.
* @param int $weekly_volume The target weekly email volume.
*
* @return array An array of created conversation IDs or WP_Error on failure.
*/
public static function generate_conversations($campaign_id, $weekly_volume) {
$conversation_ids = [];
// Example: Break down weekly volume into conversation ratios
$conversation_ratios = [
['percentage' => 5, 'participants' => [3, 6], 'replies' => [5, 12]],
['percentage' => 10, 'participants' => [3, 5], 'replies' => [3, 5]],
['percentage' => 15, 'participants' => [2, 4], 'replies' => [1, 3]],
['percentage' => 70, 'participants' => [2, 6], 'replies' => [0, 0]],
];
foreach ($conversation_ratios as $ratio) {
$volume = ceil($weekly_volume * ($ratio['percentage'] / 100));
for ($i = 0; $i < $volume; $i++) {
$conversation_steps = []; // Generate steps based on ratio
$prompt = ''; // Generate prompt for AI (to be implemented)
// Create conversation
$conversation_id = self::generate_conversation($campaign_id, $conversation_steps, $prompt);
if (is_wp_error($conversation_id)) {
return $conversation_id;
}
$steps = []; // Generate steps for this conversation
$message_result = self::generate_messages($conversation_id, $campaign_id, $steps);
if (is_wp_error($message_result)) {
return $message_result;
}
$conversation_ids[] = $conversation_id;
}
}
return $conversation_ids;
}
/**
* Generate additional email accounts for the campaign.
*
* Creates additional email accounts for initiating email conversations,
* replying to chains, and being included on CCs to increase email volume.
*
* @param int $campaign_id The campaign post ID.
* @return array List of generated email accounts.
*/
public static function generate_campaign_email_accounts($campaign_id)
{
// Implementation placeholder
return [];
}
/**
* Generate conversation vectors for the campaign.
*
* Creates a detailed map of email chains, participants, and timelines,
* based on campaign requirements.
*
* @param int $campaign_id The campaign post ID.
* @return array List of conversation vectors with timestamps and participants.
*/
public static function generate_campaign_conversation_vectors($campaign_id)
{
// Implementation placeholder
return [];
}
/**
* Generate email conversation content using ChatGPT.
*
* Creates realistic email conversations with context and replies,
* leveraging ChatGPT for natural language generation.
*
* @param int $campaign_id The campaign post ID.
* @param array $vector Details of the conversation vector (participants, topics, etc.).
* @return string Generated email conversation content.
*/
public static function generate_email_conversation($campaign_id, $vector)
{
// Implementation placeholder
return '';
}
}
/**
* Add a meta box for generating the campaign timeline.
*/
add_action('add_meta_boxes', function () {
add_meta_box(
'generate_campaign_timeline',
__('Generate Timeline', 'rl-mailwarmer'),
'rl_mailwarmer_render_generate_timeline_box',
'campaign',
'side',
'default'
);
});
/**
* Render the "Generate Timeline" meta box.
*
* @param WP_Post $post The current post object.
*/
function rl_mailwarmer_render_generate_timeline_box($post)
{
// Add a nonce for security
wp_nonce_field('generate_timeline_nonce', 'generate_timeline_nonce_field');
// Render the form
?>
<form id="generate-timeline-form">
<p>
<button type="button" id="generate-timeline-button" class="button button-primary">
<?php esc_html_e('Generate Timeline', 'rl-mailwarmer'); ?>
</button>
</p>
<div id="generate-timeline-result"></div>
</form>
<?php
}
add_action('admin_enqueue_scripts', function ($hook) {
global $post;
// Ensure this is the Add/Edit Campaign screen
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
wp_enqueue_script(
'rl-mailwarmer-generate-timeline-js',
RL_MAILWARMER_URL . 'js/generate-timeline.js', // Adjust path as needed
['jquery'],
null,
true
);
wp_localize_script('rl-mailwarmer-generate-timeline-js', 'rlMailWarmerGenerateTimeline', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('generate_timeline_nonce'),
]);
}
});
add_action('wp_ajax_rl_mailwarmer_generate_timeline', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'generate_timeline_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Validate and sanitize input
$post_id = intval($_POST['post_id']);
if (!$post_id || get_post_type($post_id) !== 'campaign') {
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
}
// Generate the timeline
try {
$timeline = RL_MailWarmer_Campaign_Helper::calculate_campaign_timeline($post_id);
// log_to_file("wp_ajax_rl_mailwarmer_generate_timeline - Generated Timeline: ", $timeline);
// update_post_meta($post_id, "campaign_timeline", $timeline);
wp_send_json_success($timeline);
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
});
// Cal-Heatmap
add_action('edit_form_after_title', function ($post) {
if ($post->post_type === 'campaign') {
echo '<div id="campaign-timeline-heatmap" style="width: 100%; height: 300px; border:1px solid #fff;"></div>';
}
});
add_action('admin_enqueue_scripts', function ($hook) {
global $post;
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
// Enqueue D3.js (required for Cal-Heatmap)
wp_enqueue_script(
'd3-js',
'https://d3js.org/d3.v7.min.js',
[],
'7.0.0',
true
);
// Enqueue Cal-Heatmap
wp_enqueue_script(
'cal-heatmap',
'https://unpkg.com/cal-heatmap/dist/cal-heatmap.min.js',
['d3-js'],
'4.2.4',
true
);
// Enqueue Cal-Heatmap CSS
wp_enqueue_style(
'cal-heatmap',
'https://unpkg.com/cal-heatmap/dist/cal-heatmap.css',
[],
'4.2.4'
);
// Custom heatmap script
wp_enqueue_script(
'rl-mailwarmer-campaign-heatmap',
RL_MAILWARMER_URL . 'js/campaign-timeline-heatmap.js',
['cal-heatmap', 'jquery'],
null,
true
);
// Pass data to the script
wp_localize_script('rl-mailwarmer-campaign-heatmap', 'rlMailWarmerHeatmap', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('campaign_timeline_nonce'),
'post_id' => $post->ID,
]);
}
});
add_action('wp_ajax_rl_mailwarmer_get_timeline', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'campaign_timeline_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Validate and sanitize input
$post_id = intval($_POST['post_id']);
if (!$post_id || get_post_type($post_id) !== 'campaign') {
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
}
// Retrieve the timeline
$timeline_json = get_post_meta($post_id, 'campaign_timeline', true);
$timeline = json_decode($timeline_json, true);
if (!$timeline) {
wp_send_json_error(__('No timeline data found.', 'rl-mailwarmer'));
}
// Convert timeline to Cal-Heatmap format (timestamp => value)
$heatmap_data = [];
foreach ($timeline as $date => $volume) {
$timestamp = strtotime($date); // Convert to UNIX timestamp
$heatmap_data[$timestamp] = $volume;
}
wp_send_json_success($heatmap_data);
});

View file

@ -0,0 +1,111 @@
<?php
class RL_MailWarmer_DB_Helper {
private static $conversation_table = 'rl_mailwarmer_conversation';
private static $message_table = 'rl_mailwarmer_message';
/**
* Create necessary database tables.
*/
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// Conversation table
$conversation_sql = "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}" . self::$conversation_table . "` (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
campaign_id BIGINT(20) UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
conversation_steps JSON NOT NULL,
prompt TEXT NOT NULL,
PRIMARY KEY (id),
INDEX campaign_id_idx (campaign_id)
) $charset_collate;";
// Message table
$message_sql = "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}" . self::$message_table . "` (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
campaign_id BIGINT(20) UNSIGNED NOT NULL,
conversation_id BIGINT(20) UNSIGNED NOT NULL,
scheduled_for_timestamp DATETIME NOT NULL,
status ENUM('pending', 'in_progress', 'sent', 'failed') NOT NULL DEFAULT 'pending',
from_email VARCHAR(255) NOT NULL,
to_email TEXT NOT NULL,
cc TEXT NULL,
subject VARCHAR(255) NOT NULL,
body TEXT NOT NULL,
PRIMARY KEY (id),
INDEX scheduled_idx (scheduled_for_timestamp, status),
INDEX conversation_id_idx (conversation_id),
INDEX campaign_id_idx (campaign_id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($conversation_sql);
dbDelta($message_sql);
}
/**
* Insert a conversation record.
*/
public static function insert_conversation($campaign_id, $conversation_steps, $prompt) {
global $wpdb;
$wpdb->insert(
"{$wpdb->prefix}" . self::$conversation_table,
[
'campaign_id' => $campaign_id,
'conversation_steps' => json_encode($conversation_steps),
'prompt' => $prompt,
],
['%d', '%s', '%s']
);
return $wpdb->insert_id;
}
/**
* Insert a message record.
*/
public static function insert_message($campaign_id, $conversation_id, $scheduled_for, $from_email, $to_email, $cc, $subject, $body) {
global $wpdb;
$wpdb->insert(
"{$wpdb->prefix}" . self::$message_table,
[
'campaign_id' => $campaign_id,
'conversation_id' => $conversation_id,
'scheduled_for_timestamp' => $scheduled_for,
'status' => 'pending',
'from_email' => $from_email,
'to_email' => $to_email,
'cc' => $cc,
'subject' => $subject,
'body' => $body,
],
['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
);
return $wpdb->insert_id;
}
/**
* Fetch pending messages.
*/
public static function fetch_pending_messages($limit = 100) {
global $wpdb;
$sql = $wpdb->prepare(
"SELECT * FROM `{$wpdb->prefix}" . self::$message_table . "`
WHERE scheduled_for_timestamp <= %s AND status = 'pending'
LIMIT %d",
current_time('mysql'),
$limit
);
return $wpdb->get_results($sql, ARRAY_A);
}
}

View file

@ -204,8 +204,11 @@ class RL_MailWarmer_Domain_Helper
*/
private static function backup_dns_record($record, $domain_id)
{
// Define the backup storage method (e.g., custom table, file, or post type)
// Example: Saving backups as posts in a custom post type
/*
* TODO: Move records to a separate table; only save a new backup if the changes are diff-
* erent than the last backup; add roll-back system
*/
$post_data = [
'post_type' => 'dns_record_backup',
@ -1118,5 +1121,94 @@ class RL_MailWarmer_Domain_Helper
return $results;
}
/*
* TO DO!
*
*/
// modify_domain_on_server - Creates/Updates/Deletes the specified domain on the specified server if it doesn't already exist (VirtualMin). Checks/Updates DNS config to include server IP in SPF, DKIM key, etc.
/**
* Modify a domain account on a VirtualMin server.
*
* Creates, updates, or deletes a virtual server for the specified domain.
*
* @param int $domain_id The domain post ID.
* @param string $action The action to perform: 'create', 'update', or 'delete'.
* @return bool|WP_Error True on success, WP_Error on failure.
*/
public static function modify_domain_account_on_server($domain_id, $action)
{
// Validate domain post
$domain = get_post($domain_id);
if (!$domain || $domain->post_type !== 'domain') {
return new WP_Error('invalid_domain', __('Invalid domain post.', 'rl-mailwarmer'));
}
// Fetch associated server posts
$server_ids = get_post_meta($domain_id, 'associated_servers', true); // Assume this holds server IDs
if (empty($server_ids) || !is_array($server_ids)) {
return new WP_Error('missing_servers', __('No associated servers found for the domain.', 'rl-mailwarmer'));
}
$domain_name = $domain->post_title;
// Iterate over servers and perform the action
foreach ($server_ids as $server_id) {
$server = get_post($server_id);
if (!$server || $server->post_type !== 'server') {
continue; // Skip invalid server posts
}
$server_ip = get_post_meta($server_id, 'ip_address', true);
$server_user = get_post_meta($server_id, 'username', true);
$server_port = get_post_meta($server_id, 'ssh_port', true) ?: 22;
$server_password = get_post_meta($server_id, 'ssh_private_key', true);
if (!$server_ip || !$server_user || !$server_password) {
return new WP_Error('missing_server_credentials', __('Missing server credentials.', 'rl-mailwarmer'));
}
// Build VirtualMin command
$command = "virtualmin";
if ($action === 'create') {
$command .= " create-domain --domain $domain_name --unix --dir --web --email";
} elseif ($action === 'update') {
$command .= " modify-domain --domain $domain_name";
} elseif ($action === 'delete') {
$command .= " delete-domain --domain $domain_name";
} else {
return new WP_Error('invalid_action', __('Invalid action specified.', 'rl-mailwarmer'));
}
// Execute the command via SSH
// $ssh = new phpseclib3\Net\SSH2($server_ip, $ssh_port);
// $key = phpseclib3\Crypt\PublicKeyLoader::loadPrivateKey($server_password); // Adjust for SSH key or plain password
$ssh = new SSH2($server_ip, $server_port);
if (!empty($server_password)) {
// Load the private key from the postmeta field
$key = PublicKeyLoader::loadPrivateKey($server_password);
} else {
// Fallback to password-based authentication
// $key = $server_password;
log_to_file("modify_domain_account_on_server - Server $$server_id ssh_private_key empty");
return new WP_Error('ssh_login_failed', __('No private key found!', 'rl-mailwarmer'));
}
if (!$ssh->login($server_user, $key)) {
return new WP_Error('ssh_login_failed', __('Failed to log into the server.', 'rl-mailwarmer'));
}
$output = $ssh->exec($command);
if (strpos($output, 'failed') !== false) {
return new WP_Error('command_failed', __('Failed to execute VirtualMin command.', 'rl-mailwarmer'));
}
}
return true; // Success
}
}

View file

@ -0,0 +1,471 @@
<?php
/**
* Helper functions for the email-account post type.
*/
require 'vendor/autoload.php';
use phpseclib3\Net\SSH2;
use phpseclib3\Crypt\PublicKeyLoader;
if (!defined('ABSPATH')) {
exit;
}
/**
* RL_MailWarmer_Email_Helper Class
*
* Handles email account management and mail operations.
*/
class RL_MailWarmer_Email_Helper
{
/**
* Modify an email account post.
*
* Creates, updates, or deletes an email account post and its associated metadata.
*
* @param array $args {
* Arguments for modifying the email account.
*
* @type int $post_id Optional. Post ID to update or delete. Required for updates or deletions.
* @type string $action Required. Action to perform: 'create', 'update', or 'delete'.
* @type string $email Optional. Email address for the account (used as post title).
* @type array $metadata Optional. Additional metadata to save with the post.
* }
* @return int|bool Post ID on success, false on failure.
*/
// Create an Email Account
// $post_id = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'create',
// 'email' => 'johndoe@example.com',
// 'metadata' => [
// 'full_name' => 'John Doe',
// 'mail_password' => 'securepassword123',
// 'email_provider' => 123, // Post ID of the email provider
// 'smtp_server' => 'smtp.example.com',
// 'smtp_port' => 587,
// 'imap_server' => 'imap.example.com',
// 'imap_port' => 993,
// ],
// ]);
// // Update an Email Account
// $result = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'update',
// 'post_id' => $post_id,
// 'metadata' => [
// 'full_name' => 'Jane Doe',
// 'mail_password' => 'newsecurepassword123',
// 'smtp_status' => 'connected',
// ],
// ]);
// // Delete an Email Account
// $result = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'delete',
// 'post_id' => $post_id,
// ]);
public static function modify_email_account(array $args)
{
// Validate required arguments
if (empty($args['action']) || !in_array($args['action'], ['create', 'update', 'delete'], true)) {
throw new InvalidArgumentException('Invalid or missing action.');
}
/*
* Add validation to only delete email-account posts
*
*/
$action = $args['action'];
$post_id = $args['post_id'] ?? null;
// Handle delete action
if ($action === 'delete') {
if (!$post_id) {
throw new InvalidArgumentException('Post ID is required for deletion.');
}
return wp_delete_post($post_id, true);
}
// Validate fields for create/update
$post_data = [
'post_type' => 'email-account',
'post_status' => 'publish',
];
// For "create", ensure no existing post with the same title
if ($action === 'create') {
if (empty($args['email'])) {
throw new InvalidArgumentException('Email is required for creating a new account.');
}
$existing_post = get_page_by_title($args['email'], OBJECT, 'email-account');
if ($existing_post) {
throw new RuntimeException('An email account with this title already exists.');
}
$post_data['post_title'] = $args['email'];
} elseif ($action === 'update') {
if (!$post_id) {
throw new InvalidArgumentException('Post ID is required for updates.');
}
$post_data['ID'] = $post_id;
}
// Assemble metadata
$meta_args = $args['metadata'] ?? [];
// Generate a random password if mail_password is not provided
if (empty($meta_args['mail_password'])) {
$meta_args['mail_password'] = bin2hex(random_bytes(8)); // 16-character password
}
$post_data['meta_input'] = array_map('sanitize_text_field', $meta_args);
// Save or update the post
$post_id = wp_insert_post($post_data);
if (is_wp_error($post_id)) {
throw new RuntimeException('Failed to save email account post: ' . $post_id->get_error_message());
}
return $post_id;
}
/**
* Check mail login credentials for an email account.
*
* Validates IMAP/SMTP connection settings for the given email account.
*
* @param mixed $email_account The email-account post object or ID.
* @param string|null $protocol Optional. The protocol to validate ('IMAP' or 'SMTP'). Defaults to both.
* @return array|WP_Error Validation results for IMAP and/or SMTP or WP_Error on failure.
*/
public static function check_mail_login($email_account, $protocol = null)
{
// Get the post object
$post = is_numeric($email_account) ? get_post($email_account) : $email_account;
if (!$post || $post->post_type !== 'email-account') {
return new WP_Error('invalid_post', __('Invalid email account post.', 'rl-mailwarmer'));
}
// Fetch email provider and override defaults with saved values
$email_provider_id = get_post_meta($post->ID, 'email_provider', true);
// log_to_file("check_mail_login - ");
log_to_file("check_mail_login - Email Provider ID $email_provider_id");
$defaults = $email_provider_id ? self::get_provider_defaults($email_provider_id) : [];
log_to_file("check_mail_login - Email Provider Defaults: ", $defaults);
// Fetch saved settings
$saved_settings = [
'mail_password' => get_post_meta($post->ID, 'mail_password', true),
'imap_password' => get_post_meta($post->ID, 'imap_password', true),
'imap_server' => get_post_meta($post->ID, 'imap_server', true),
'imap_port' => get_post_meta($post->ID, 'imap_port', true),
'smtp_password' => get_post_meta($post->ID, 'smtp_password', true),
'smtp_server' => get_post_meta($post->ID, 'smtp_server', true),
'smtp_port' => get_post_meta($post->ID, 'smtp_port', true),
];
// Merge saved settings with defaults
$settings = array_merge($defaults, array_filter($saved_settings));
log_to_file("check_mail_login - Using settings: ", $settings);
$results = [];
// Validate IMAP connection if required
if ($protocol === null || strtoupper($protocol) === 'IMAP') {
$imap_result = self::validate_imap_connection($post->post_title, $settings);
log_to_file("check_mail_login - IMAP Result for " . $post->post_title . ": ", $imap_result);
$results['IMAP'] = $imap_result ? __('IMAP connection successful.', 'rl-mailwarmer') : __('IMAP connection failed.', 'rl-mailwarmer');
}
// Validate SMTP connection if required
if ($protocol === null || strtoupper($protocol) === 'SMTP') {
$smtp_result = self::validate_smtp_connection($post->post_title, $settings);
log_to_file("check_mail_login - SMTP Result for " . $post->post_title . ": ", $imap_result);
$results['SMTP'] = $smtp_result ? __('SMTP connection successful.', 'rl-mailwarmer') : __('SMTP connection failed.', 'rl-mailwarmer');
}
log_to_file("check_mail_login - Full Results for " . $post->post_title . ": ", $results);
return $results;
}
/**
* Fetch default settings for an email provider.
*
* @param int $email_provider_id The post ID of the email provider.
* @return array The default server settings.
*/
private static function get_provider_defaults($email_provider_id)
{
return [
'imap_server' => get_post_meta($email_provider_id, 'default_imap_server', true),
'imap_port' => get_post_meta($email_provider_id, 'default_imap_port', true),
'smtp_server' => get_post_meta($email_provider_id, 'default_smtp_server', true),
'smtp_port' => get_post_meta($email_provider_id, 'default_smtp_port', true),
];
}
/**
* Validate an IMAP connection for an email account.
*
* @param string $email The email address.
* @param array $settings The server settings (imap_server, imap_port, imap_password).
* @return bool True if the connection is successful, false otherwise.
*/
private static function validate_imap_connection($email, $settings)
{
if ( empty($settings['imap_server']) || empty($settings['imap_port']) ) {
return false; // Missing required settings
}
if (!empty($settings['imap_password'])) {
$password = $settings['imap_password'];
} else {
$password = $settings['mail_password'];
}
// Try connecting to the IMAP server
$imap_stream = @imap_open(
'{' . $settings['imap_server'] . ':' . $settings['imap_port'] . '/imap/ssl}',
$email,
$password
);
if ($imap_stream) {
imap_close($imap_stream); // Close connection if successful
return true;
}
return false; // Connection failed
}
/**
* Validate an SMTP connection for an email account using Symfony Mailer.
*
* @param string $email The email address.
* @param array $settings The server settings (smtp_server, smtp_port, smtp_password).
* @return bool True if the connection is successful, false otherwise.
*/
private static function validate_smtp_connection($email, $settings)
{
if (empty($settings['smtp_server']) || empty($settings['smtp_port']) ) {
return false; // Missing required settings
}
if (!empty($settings['smtp_password'])) {
$password = $settings['smtp_password'];
} else {
$password = $settings['mail_password'];
}
$test_to_email = "ruben@redlotusaustin.com";
try {
// Create the SMTP transport
$transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
$settings['smtp_server'],
$settings['smtp_port']
);
// Set authentication details
$transport->setUsername($email);
$transport->setPassword($password);
// Create the mailer
$mailer = new Symfony\Component\Mailer\Mailer($transport);
// Send a test email
$test_email = (new Symfony\Component\Mime\Email())
->from($email)
->to($test_to_email)
->subject('SMTP Connection for ' . $email)
->text('This is a test email to verify SMTP connection for ' . $email);
$mailer->send($test_email);
return true;
} catch (Exception $e) {
error_log('SMTP validation failed: ' . $e->getMessage());
return false;
}
}
/**
* Generate random email accounts for the specified domain.
*
* @param string $domain The domain name to use for the email accounts.
* @param int $qty The number of email accounts to generate. Defaults to 1.
* @return array List of generated names and email addresses.
* @throws Exception If name pools are empty or post creation fails.
*/
public static function generate_random_accounts($domain, $qty = 1)
{
// Fetch name pools from ACF options
$first_name_pool = get_field('valid_first_name_pool', 'option');
$last_name_pool = get_field('valid_last_name_pool', 'option');
if (empty($first_name_pool) || empty($last_name_pool)) {
throw new Exception(__('Name pools are empty. Please configure them in the ACF options.', 'rl-mailwarmer'));
}
$first_names = explode(',', $first_name_pool); // Assume comma-separated list
$last_names = explode(',', $last_name_pool);
$generated_accounts = [];
for ($i = 0; $i < $qty; $i++) {
// Generate a random name
$first_name = trim($first_names[array_rand($first_names)]);
$last_name = trim($last_names[array_rand($last_names)]);
$full_name = "{$first_name} {$last_name}";
// Generate a semi-random email address
$email_formats = [
"{$first_name}{$last_name}",
"{$first_name}.{$last_name}",
substr($first_name, 0, 1) . ".{$last_name}",
"{$first_name}.l",
substr($first_name, 0, 1) . $last_name,
];
$email_local_part = strtolower($email_formats[array_rand($email_formats)]);
$email_address = "{$email_local_part}@{$domain}";
// Create the email-account post
// $post_id = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'create',
// 'email' => $email_address,
// 'metadata' => [
// 'full_name' => $full_name,
// 'mail_password' => bin2hex(random_bytes(8)), // Generate a secure random password
// ],
// ]);
// if (!$post_id) {
// throw new Exception(__('Failed to create email account post.', 'rl-mailwarmer'));
// }
// Add to results
$generated_accounts[] = [
'full_name' => $full_name,
'email_address' => $email_address,
];
}
return $generated_accounts;
}
/**
* Modify an email account on a VirtualMin server.
*
* @param int $account_id The email-account post ID.
* @param string $action The action to perform: 'create', 'update', or 'delete'.
* @return bool|WP_Error True on success, WP_Error on failure.
*/
public static function modify_email_account_on_server($account_id, $action)
{
// Validate email-account post
$email_account = get_post($account_id);
if (!$email_account || $email_account->post_type !== 'email-account') {
return new WP_Error('invalid_account', __('Invalid email account.', 'rl-mailwarmer'));
}
// Fetch associated server posts
$domain_id = get_post_meta($account_id, 'domain_id', true);
if (!$domain_id) {
return new WP_Error('missing_domain', __('No associated domain found.', 'rl-mailwarmer'));
}
$server_ids = get_post_meta($domain_id, 'associated_servers', true); // Assuming this field holds server post IDs
if (empty($server_ids) || !is_array($server_ids)) {
return new WP_Error('missing_servers', __('No associated servers found.', 'rl-mailwarmer'));
}
// Fetch email account details
$email_address = $email_account->post_title;
$password = get_post_meta($account_id, 'mail_password', true);
[$username, $domain] = explode('@', $email_address);
// Iterate over servers and perform the action
foreach ($server_ids as $server_id) {
$server = get_post($server_id);
if (!$server || $server->post_type !== 'server') {
continue; // Skip invalid server posts
}
$server_ip = get_post_meta($server_id, 'ip_address', true);
$server_port = get_post_meta($server_id, 'ssh_port', true);
$server_user = get_post_meta($server_id, 'username', true);
$server_password = get_post_meta($server_id, 'ssh_private_key', true);
// $server_password = get_post_meta($server_id, 'password', true);
if (!$server_ip || !$server_user || !$server_password) {
return new WP_Error('missing_server_details', __('Missing server credentials.', 'rl-mailwarmer'));
}
// Build VirtualMin command
$command = "virtualmin";
if ($action === 'create') {
$command .= " create-user --domain $domain --user $username --pass $password";
} elseif ($action === 'update') {
$command .= " modify-user --domain $domain --user $username --pass $password";
} elseif ($action === 'delete') {
$command .= " delete-user --domain $domain --user $username";
} else {
return new WP_Error('invalid_action', __('Invalid action specified.', 'rl-mailwarmer'));
}
// Execute the command via SSH
// $ssh = new phpseclib\Net\SSH2($server_ip);
// $key = new phpseclib\Crypt\PublicKeyLoader::loadPrivateKey($server_password); // Adjust for SSH key or plain password
$ssh = new SSH2($server_ip, $server_port);
if (!empty($server_password)) {
// Load the private key from the postmeta field
$key = PublicKeyLoader::loadPrivateKey($server_password);
} else {
// Fallback to password-based authentication
// $key = $server_password;
log_to_file("modify_email_account_on_server - Server $$server_id ssh_private_key empty");
return new WP_Error('ssh_login_failed', __('No private key found!', 'rl-mailwarmer'));
}
if (!$ssh->login($server_user, $key)) {
return new WP_Error('ssh_login_failed', __('Failed to log into the server.', 'rl-mailwarmer'));
}
$output = $ssh->exec($command);
if (strpos($output, 'failed') !== false) {
return new WP_Error('command_failed', __('Failed to execute VirtualMin command.', 'rl-mailwarmer'));
}
}
return true; // Success
}
/**
* Send or reply to a conversation email.
*
* @param int $conversation_id The conversation post ID.
* @param int $account_id The email account post ID.
* @return bool True on success, false on failure.
*/
public static function send_conversation_mail(int $conversation_id, int $account_id)
{
// Implementation goes here
}
}

View file

@ -1,5 +1,8 @@
{
"require": {
"guzzlehttp/guzzle": "^7.9"
"guzzlehttp/guzzle": "^7.9",
"symfony/mailer": "^7.2",
"symfony/http-client": "^7.2",
"phpseclib/phpseclib": "^3.0"
}
}

1394
includes/composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -177,3 +177,52 @@ add_action('wp_ajax_rl_mailwarmer_create_dns_backup', function () {
wp_send_json_error($e->getMessage());
}
});
add_action('wp_ajax_rl_mailwarmer_modify_email_account', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'modify_email_account_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Prepare arguments for modify_email_account
$args = [
'action' => sanitize_text_field($_POST['action_type']),
'email' => sanitize_email($_POST['email_address']),
'metadata' => [
'mail_password' => sanitize_text_field($_POST['mail_password']),
'email_provider' => intval($_POST['email_provider']),
'imap_server' => sanitize_text_field($_POST['imap_server']),
],
];
// Call modify_email_account
try {
$result = RL_MailWarmer_Email_Helper::modify_email_account($args);
wp_send_json_success('Email account successfully modified. Post ID: ' . $result);
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
});
add_action('wp_ajax_rl_mailwarmer_check_mail_login', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'check_mail_login_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Validate and sanitize input
$post_id = intval($_POST['post_id']);
$protocol = !empty($_POST['protocol']) ? sanitize_text_field($_POST['protocol']) : null;
if (!$post_id || get_post_type($post_id) !== 'email-account') {
wp_send_json_error(__('Invalid email account ID.', 'rl-mailwarmer'));
}
// Call check_mail_login
try {
$results = RL_MailWarmer_Email_Helper::check_mail_login($post_id, $protocol);
wp_send_json_success($results);
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
});

View file

@ -46,7 +46,63 @@ add_action('admin_enqueue_scripts', function ($hook) {
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('create_dns_backup_nonce'),
]);
}
} else if ($post->post_type === 'email-account') {
wp_enqueue_script(
'rl-mailwarmer-check-login-js',
RL_MAILWARMER_URL . 'js/check-mail-login.js', // Adjust path as needed
['jquery'],
null,
true
);
wp_localize_script('rl-mailwarmer-check-login-js', 'rlMailWarmerCheckMailLogin', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('check_mail_login_nonce'),
'post_id' => $post->ID
]);
} else if ($post->post_type === 'campaign') {
// log_to_file("On a campaign page. Enqueing admin scripts");
// // Enqueue D3.js
// wp_enqueue_script(
// 'd3-js',
// 'https://d3js.org/d3.v7.min.js',
// [],
// '7.0.0',
// true
// );
// // Enqueue Cal-Heatmap script and style
// wp_enqueue_script(
// 'cal-heatmap',
// 'https://unpkg.com/cal-heatmap/dist/cal-heatmap.min.js',
// [],
// '4.2.4',
// true
// );
// wp_enqueue_style(
// 'cal-heatmap',
// 'https://unpkg.com/cal-heatmap/dist/cal-heatmap.css',
// [],
// '4.2.4'
// );
// // Enqueue custom script for rendering the heatmap
// wp_enqueue_script(
// 'rl-mailwarmer-campaign-heatmap',
// RL_MAILWARMER_URL . 'js/campaign-timeline-heatmap.js', // Adjust the path as needed
// ['cal-heatmap', 'jquery'],
// null,
// true
// );
// // Pass data to the custom script
// wp_localize_script('rl-mailwarmer-campaign-heatmap', 'rlMailWarmerHeatmap', [
// 'ajax_url' => admin_url('admin-ajax.php'),
// 'nonce' => wp_create_nonce('campaign_timeline_nonce'),
// 'post_id' => $post->ID,
// ]);
}
}
});

View file

@ -0,0 +1,131 @@
<?php
add_action('admin_enqueue_scripts', function () {
wp_enqueue_script(
'rl-mailwarmer-email-account-js',
RL_MAILWARMER_URL . '/js/email-account.js',
['jquery'],
null,
true
);
wp_localize_script('rl-mailwarmer-email-account-js', 'rlMailWarmerEmailAccount', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('modify_email_account_nonce'),
]);
});
/**
* Add a custom metabox to the WP dashboard.
*/
add_action('wp_dashboard_setup', function () {
wp_add_dashboard_widget(
'modify_email_account_widget', // Widget ID
__('Modify Email Account', 'rl-mailwarmer'), // Title
'rl_mailwarmer_render_email_account_widget' // Callback function
);
});
/**
* Add a meta box for checking mail login.
*/
add_action('add_meta_boxes', function () {
add_meta_box(
'check_mail_login_box',
__('Check Mail Login', 'rl-mailwarmer'),
'rl_mailwarmer_render_check_mail_login_box',
'email-account',
'side',
'default'
);
});
/**
* Render the Modify Email Account metabox.
*/
function rl_mailwarmer_render_email_account_widget()
{
// Fetch email providers
$email_providers = get_posts([
'post_type' => 'email-provider',
'post_status' => 'publish',
'numberposts' => -1,
'orderby' => 'title',
'order' => 'ASC',
]);
// Render the form
?>
<form id="modify-email-account-form">
<p>
<label for="email_address"><?php esc_html_e('Email Address', 'rl-mailwarmer'); ?></label>
<input type="email" id="email_address" name="email_address" class="regular-text" required>
</p>
<p>
<label for="mail_password"><?php esc_html_e('Password (Leave empty for random)', 'rl-mailwarmer'); ?></label>
<input type="password" id="mail_password" name="mail_password" class="regular-text">
</p>
<p>
<label for="action"><?php esc_html_e('Action', 'rl-mailwarmer'); ?></label>
<select id="action" name="action" required>
<option value="create"><?php esc_html_e('Create', 'rl-mailwarmer'); ?></option>
<option value="update"><?php esc_html_e('Update', 'rl-mailwarmer'); ?></option>
<option value="delete"><?php esc_html_e('Delete', 'rl-mailwarmer'); ?></option>
</select>
</p>
<p>
<label for="email_provider"><?php esc_html_e('Email Provider', 'rl-mailwarmer'); ?></label>
<select id="email_provider" name="email_provider">
<option value=""><?php esc_html_e('-- Select Provider --', 'rl-mailwarmer'); ?></option>
<?php foreach ($email_providers as $provider): ?>
<option value="<?php echo esc_attr($provider->ID); ?>">
<?php echo esc_html($provider->post_title); ?>
</option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="imap_server"><?php esc_html_e('IMAP Server', 'rl-mailwarmer'); ?></label>
<input type="text" id="imap_server" name="imap_server" class="regular-text">
</p>
<p>
<button type="button" id="submit-email-account" class="button button-primary">
<?php esc_html_e('Submit', 'rl-mailwarmer'); ?>
</button>
</p>
<div id="modify-email-account-result"></div>
</form>
<?php
}
/**
* Render the meta box for checking mail login.
*
* @param WP_Post $post The current post object.
*/
function rl_mailwarmer_render_check_mail_login_box($post)
{
// Add a nonce field for security
wp_nonce_field('check_mail_login_nonce', 'check_mail_login_nonce_field');
// Render the form
?>
<form id="check-mail-login-form">
<p>
<label for="protocol"><?php esc_html_e('Protocol', 'rl-mailwarmer'); ?></label>
<select id="protocol" name="protocol" class="regular-text">
<option value=""><?php esc_html_e('-- Select Protocol --', 'rl-mailwarmer'); ?></option>
<option value="IMAP"><?php esc_html_e('IMAP', 'rl-mailwarmer'); ?></option>
<option value="SMTP"><?php esc_html_e('SMTP', 'rl-mailwarmer'); ?></option>
</select>
</p>
<p>
<button type="button" id="check-mail-login-button" class="button button-primary">
<?php esc_html_e('Check Login', 'rl-mailwarmer'); ?>
</button>
</p>
<div id="check-mail-login-result"></div>
</form>
<?php
}

View file

@ -26,51 +26,107 @@ function log_to_file($message = false, $data = false){
}
/**
* Save a domain health report when a domain is first published.
*
* @param string $new_status The new post status.
* @param string $old_status The old post status.
* @param WP_Post $post The post object.
*/
// add_action('transition_post_status', function ($new_status, $old_status, $post) {
// // Ensure we're working with the 'domain' post type
// if ($post->post_type !== 'domain') {
// return;
// }
// // Only run when the status changes to 'publish' from a non-published status
// if ($new_status === 'publish' && $old_status !== 'publish') {
// try {
// RL_MailWarmer_Domain_Helper::saveDomainHealthReport($post->ID);
// } catch (Exception $e) {
// error_log('Failed to save domain health report: ' . $e->getMessage());
// }
// }
// }, 10, 3);
/**
* Save a domain health report when a new domain is created.
*
* @param int $post_id The ID of the post being saved.
* @param WP_Post $post The post object.
* @param bool $update Whether this is an update.
* Add a meta box for testing the SSH connection.
*/
// add_action('save_post_domain', function ($post_id, $post, $update) {
// // Exclude autosaves, revisions, drafts, and updates
// if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id) || $post->post_status === 'draft' || $update) {
// return;
// }
add_action('add_meta_boxes', function () {
add_meta_box(
'test_ssh_connection_box',
__('Test SSH Connection', 'rl-mailwarmer'),
'rl_mailwarmer_render_test_ssh_connection_box',
'server',
'side',
'default'
);
});
/**
* Render the SSH connection test meta box.
*
* @param WP_Post $post The current post object.
*/
function rl_mailwarmer_render_test_ssh_connection_box($post)
{
// Add a nonce field for security
wp_nonce_field('test_ssh_connection_nonce', 'test_ssh_connection_nonce_field');
// Render the button
?>
<form id="test-ssh-connection-form">
<p>
<button type="button" id="test-ssh-connection-button" class="button button-primary">
<?php esc_html_e('Test SSH Connection', 'rl-mailwarmer'); ?>
</button>
</p>
<div id="test-ssh-connection-result"></div>
</form>
<?php
}
add_action('admin_enqueue_scripts', function ($hook) {
global $post;
// Ensure this is the Add/Edit Server screen
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'server') {
wp_enqueue_script(
'rl-mailwarmer-test-ssh-js',
RL_MAILWARMER_URL . 'js/test-ssh-connection.js', // Adjust path as needed
['jquery'],
null,
true
);
wp_localize_script('rl-mailwarmer-test-ssh-js', 'rlMailWarmerTestSSH', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('test_ssh_connection_nonce'),
'post_id' => $post->ID
]);
}
});
add_action('wp_ajax_rl_mailwarmer_test_ssh_connection', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'test_ssh_connection_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Validate and sanitize input
$post_id = intval($_POST['post_id']);
if (!$post_id || get_post_type($post_id) !== 'server') {
wp_send_json_error(__('Invalid server ID.', 'rl-mailwarmer'));
}
// Fetch server details
$server_ip = get_post_meta($post_id, 'ip_address', true);
$server_port = get_post_meta($post_id, 'ssh_port', true);
$server_user = get_post_meta($post_id, 'username', true);
$server_key = get_post_meta($post_id, 'ssh_private_key', true);
// log_to_file("wp_ajax_rl_mailwarmer_test_ssh_connection - SSH $server_user $server_ip $server_port");
// log_to_file("wp_ajax_rl_mailwarmer_test_ssh_connection - SSH Private Key: $server_key");
if (empty($server_ip) || empty($server_user) || empty($server_key)) {
wp_send_json_error(__('Missing server credentials.', 'rl-mailwarmer'));
}
// Test SSH connection
try {
$ssh = new phpseclib3\Net\SSH2($server_ip, $server_port);
$key = phpseclib3\Crypt\PublicKeyLoader::loadPrivateKey($server_key);
if (!$ssh->login($server_user, $key)) {
throw new Exception(__('SSH login failed.', 'rl-mailwarmer'));
}
wp_send_json_success(__('SSH connection successful.', 'rl-mailwarmer'));
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
});
// // Call saveDomainHealthReport
// try {
// log_to_file("save_post_domain - Running health report for newly added domain: " . $post->post_title);
// RL_MailWarmer_Domain_Helper::saveDomainHealthReport($post_id);
// } catch (Exception $e) {
// error_log('Failed to save domain health report: ' . $e->getMessage());
// }
// log_to_file("save_post_domain - Finished! " . $post->post_title);
// }, 10, 3);

View file

@ -0,0 +1,65 @@
jQuery(document).ready(function ($) {
// Wait for the DOM to be fully loaded
$(window).on('load', function () {
// Ensure the element exists before proceeding
const heatmapContainer = $('#campaign-timeline-heatmap');
if (!heatmapContainer.length) {
console.error('Heatmap container not found.');
return;
}
// Fetch campaign timeline data via AJAX
$.ajax({
url: rlMailWarmerHeatmap.ajax_url,
method: 'POST',
data: {
action: 'rl_mailwarmer_get_timeline',
post_id: rlMailWarmerHeatmap.post_id,
security: rlMailWarmerHeatmap.nonce,
},
success: function (response) {
if (response.success) {
console.log(response.data);
// Initialize the heatmap
const cal = new CalHeatmap();
cal.paint({
data: {
source: response.data,
x: 'date',
y: d => +d['volume'],
},
date: {
start: new Date('2024-12-4'),
},
range: 1, // Number of months to display
domain: { type: 'year' },
subDomain: { type: 'day' }, // Granularity: days
legend: {
show: true,
label: 'Daily Volume',
width: 150,
marginLeft: 10,
marginRight: 10,
},
scale: {
color: {
range: ['yellow', 'red'],
interpolate: 'hsl',
type: 'quantize',
domain: [0, 150],
scheme: 'YlOrRd',
},
},
verticalOrientation: true,
itemSelector: '#campaign-timeline-heatmap',
});
} else {
heatmapContainer.html('<p>Error: ' + response.data + '</p>');
}
},
error: function () {
heatmapContainer.html('<p>Failed to load timeline data.</p>');
},
});
});
});

37
js/check-mail-login.js Normal file
View file

@ -0,0 +1,37 @@
jQuery(document).ready(function ($) {
$('#check-mail-login-button').on('click', function (e) {
e.preventDefault();
const postId = $('#post_ID').val();
const protocol = $('#protocol').val();
$('#check-mail-login-result').html('<p>Checking...</p>');
$.ajax({
url: rlMailWarmerCheckMailLogin.ajax_url,
method: 'POST',
data: {
action: 'rl_mailwarmer_check_mail_login',
post_id: postId,
protocol: protocol,
security: rlMailWarmerCheckMailLogin.nonce,
},
success: function (response) {
if (response.success) {
const results = response.data;
let output = '<p>Check Results:</p><ul>';
$.each(results, function (key, value) {
output += `<li><strong>${key}:</strong> ${value}</li>`;
});
output += '</ul>';
$('#check-mail-login-result').html(output);
} else {
$('#check-mail-login-result').html('<p>Error: ' + response.data + '</p>');
}
},
error: function (xhr, status, error) {
$('#check-mail-login-result').html('<p>AJAX Error: ' + error + '</p>');
},
});
});
});

33
js/email-account.js Normal file
View file

@ -0,0 +1,33 @@
jQuery(document).ready(function ($) {
$('#submit-email-account').on('click', function (e) {
e.preventDefault();
const formData = {
action: 'rl_mailwarmer_modify_email_account',
security: rlMailWarmerEmailAccount.nonce,
email_address: $('#email_address').val(),
mail_password: $('#mail_password').val(),
action_type: $('#action').val(),
email_provider: $('#email_provider').val(),
imap_server: $('#imap_server').val(),
};
$('#modify-email-account-result').html('<p>Processing...</p>');
$.ajax({
url: rlMailWarmerEmailAccount.ajax_url,
method: 'POST',
data: formData,
success: function (response) {
if (response.success) {
$('#modify-email-account-result').html('<p>Success: ' + response.data + '</p>');
} else {
$('#modify-email-account-result').html('<p>Error: ' + response.data + '</p>');
}
},
error: function (xhr, status, error) {
$('#modify-email-account-result').html('<p>AJAX Error: ' + error + '</p>');
},
});
});
});

35
js/generate-timeline.js Normal file
View file

@ -0,0 +1,35 @@
jQuery(document).ready(function ($) {
$('#generate-timeline-button').on('click', function (e) {
e.preventDefault();
const postId = $('#post_ID').val();
$('#generate-timeline-result').html('<p>Generating timeline...</p>');
$.ajax({
url: rlMailWarmerGenerateTimeline.ajax_url,
method: 'POST',
data: {
action: 'rl_mailwarmer_generate_timeline',
post_id: postId,
security: rlMailWarmerGenerateTimeline.nonce,
},
success: function (response) {
if (response.success) {
const timeline = response.data;
let output = '<p>Timeline Generated:</p><ul>';
$.each(timeline, function (date, volume) {
output += `<li><strong>${date}:</strong> ${volume} emails</li>`;
});
output += '</ul>';
$('#generate-timeline-result').html(output);
} else {
$('#generate-timeline-result').html('<p>Error: ' + response.data + '</p>');
}
},
error: function (xhr, status, error) {
$('#generate-timeline-result').html('<p>AJAX Error: ' + error + '</p>');
},
});
});
});

29
js/test-ssh-connection.js Normal file
View file

@ -0,0 +1,29 @@
jQuery(document).ready(function ($) {
$('#test-ssh-connection-button').on('click', function (e) {
e.preventDefault();
const postId = rlMailWarmerTestSSH.post_id;
$('#test-ssh-connection-result').html('<p>Testing connection...</p>');
$.ajax({
url: rlMailWarmerTestSSH.ajax_url,
method: 'POST',
data: {
action: 'rl_mailwarmer_test_ssh_connection',
post_id: postId,
security: rlMailWarmerTestSSH.nonce,
},
success: function (response) {
if (response.success) {
$('#test-ssh-connection-result').html('<p>Success: ' + response.data + '</p>');
} else {
$('#test-ssh-connection-result').html('<p>Error: ' + response.data + '</p>');
}
},
error: function (xhr, status, error) {
$('#test-ssh-connection-result').html('<p>AJAX Error: ' + error + '</p>');
},
});
});
});

View file

@ -27,10 +27,16 @@ require_once RL_MAILWARMER_PATH . 'includes/rl-mailwarmer-functions.php';
require_once RL_MAILWARMER_PATH . 'includes/rl-mailwarmer-ajax.php';
require_once RL_MAILWARMER_PATH . 'includes/rl-mailwarmer-rest.php';
require_once RL_MAILWARMER_PATH . 'includes/rl-mailwarmer-domain-admin.php';
require_once RL_MAILWARMER_PATH . 'includes/rl-mailwarmer-email-admin.php';
require_once RL_MAILWARMER_PATH . 'includes/class-rl-mailwarmer-db-helper.php';
require_once RL_MAILWARMER_PATH . 'includes/class-rl-mailwarmer-domain-helper.php';
require_once RL_MAILWARMER_PATH . 'includes/class-rl-mailwarmer-email-helper.php';
require_once RL_MAILWARMER_PATH . 'includes/class-rl-mailwarmer-campaign-helper.php';
require_once RL_MAILWARMER_PATH . 'includes/class-rl-mailwarmer-scheduler.php';
require_once RL_MAILWARMER_PATH . 'includes/class-rl-mailwarmer-email-handler.php';
// require_once RL_MAILWARMER_PATH . 'includes/vendor/autoload.php';
/**
* Initialize the plugin.
*/
@ -55,6 +61,9 @@ function rl_mailwarmer_activate()
{
// Schedule cron jobs on activation
RL_MailWarmer_Scheduler::schedule_cron_jobs();
// Create custom tables
RL_MailWarmer_DB_Helper::create_tables();
}
register_activation_hook(__FILE__, 'rl_mailwarmer_activate');