Compare commits

..

No commits in common. "master" and "warmup-accounts" have entirely different histories.

27 changed files with 874 additions and 9808 deletions

View file

@ -1,19 +0,0 @@
# RL MailWarmer WordPress Plugin Guide
## Build/Test Commands
- **Run PHPUnit Tests**: `vendor/bin/phpunit tests`
- **Run Single Test**: `vendor/bin/phpunit tests/TestName.php`
- **Composer Update**: `composer update`
- **Composer Install**: `composer install`
## Code Style Guidelines
- **Class Naming**: Use `RL_MailWarmer_ClassName` format with underscores
- **Method Naming**: Use snake_case for method names
- **File Naming**: Use `class-rl-mailwarmer-name.php` for class files
- **Database**: Use `rl_mailwarmer_` prefix for all tables
- **Error Handling**: Use WP_Error for WordPress-specific errors, Exceptions for PHP-level errors
- **Validation**: Always validate and sanitize inputs using WordPress functions
- **Documentation**: Use PHPDoc blocks for classes and methods
- **Security**: Follow WordPress security best practices (prepare SQL, validate nonces)
- **Imports**: Place includes at the top, use require_once for class dependencies
- **Type Hints**: Use PHP type hints for method parameters and return types

View file

@ -1,19 +0,0 @@
{
"require": {
"guzzlehttp/guzzle": "^7.9",
"symfony/mailer": "^7.2",
"symfony/http-client": "^7.2",
"phpseclib/phpseclib": "^3.0",
"openai-php/client": "^0.10.3",
"php-imap/php-imap": "^5.0",
"symfony/mime": "^7.2"
},
"config": {
"allow-plugins": {
"php-http/discovery": true
}
},
"require-dev": {
"phpunit/phpunit": "^11.5"
}
}

3989
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,344 +0,0 @@
/* Modal Styles */
.mf-floating-action-button {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
background-color: #0073aa;
border-radius: 50%;
text-align: center;
line-height: 60px;
font-size: 24px;
color: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
z-index: 1000;
transition: transform 0.3s, background-color 0.3s;
}
.mf-floating-action-button:hover {
transform: scale(1.1);
background-color: #005d87;
}
.mf-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(8px);
z-index: 1001;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s;
}
.mf-modal-overlay.active {
opacity: 1;
visibility: visible;
}
.mf-modal-container {
background: white;
width: 90%;
max-width: 700px;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
max-height: 90vh;
display: flex;
flex-direction: column;
}
.mf-modal-header {
padding: 20px;
background-color: #f8f8f8;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.mf-modal-header h2 {
margin: 0;
font-size: 1.4rem;
color: #333;
}
.mf-modal-close {
background: none;
border: none;
font-size: 24px;
color: #888;
cursor: pointer;
transition: color 0.3s;
}
.mf-modal-close:hover {
color: #333;
}
.mf-modal-body {
padding: 20px;
overflow-y: auto;
flex: 1;
}
.mf-modal-footer {
padding: 15px 20px;
background-color: #f8f8f8;
border-top: 1px solid #eee;
display: flex;
justify-content: space-between;
}
.mf-modal-footer button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: none;
font-weight: 500;
}
.mf-back-btn {
background-color: #f0f0f0;
color: #555;
}
.mf-next-btn {
background-color: #0073aa;
color: white;
}
.mf-back-btn:hover {
background-color: #e0e0e0;
}
.mf-next-btn:hover {
background-color: #005d87;
}
.mf-next-btn.disabled {
background-color: #ccc;
cursor: not-allowed;
}
/* Step indicator styles */
.mf-steps-indicator {
display: flex;
justify-content: center;
margin: 0 0 20px;
}
.mf-step-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #ddd;
margin: 0 5px;
transition: background-color 0.3s;
}
.mf-step-dot.active {
background-color: #0073aa;
}
.mf-step-dot.completed {
background-color: #4CAF50;
}
/* Multi-step form styles */
.mf-step {
display: none;
}
.mf-step.active {
display: block;
}
.mf-step h3 {
margin-top: 0;
color: #333;
}
.mf-form-field {
margin-bottom: 20px;
}
.mf-form-field label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.mf-form-field input,
.mf-form-field select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.mf-form-field input:focus,
.mf-form-field select:focus {
border-color: #0073aa;
outline: none;
box-shadow: 0 0 0 1px #0073aa;
}
.mf-selection-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.mf-selection-button {
flex: 1;
padding: 15px;
border: 2px solid #ddd;
border-radius: 6px;
background-color: #f8f8f8;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.mf-selection-button:hover {
border-color: #0073aa;
background-color: #f0f7fb;
}
.mf-selection-button.selected {
border-color: #0073aa;
background-color: #e6f3fa;
}
.mf-selection-button i {
display: block;
font-size: 24px;
margin-bottom: 10px;
color: #555;
}
.mf-selection-button.selected i {
color: #0073aa;
}
.mf-validation-error {
color: #d32f2f;
font-size: 14px;
margin-top: 5px;
display: none;
}
.mf-loading {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.mf-loading-spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #0073aa;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin-right: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.mf-success-message,
.mf-error-message {
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.mf-success-message {
background-color: #e8f5e9;
border: 1px solid #c8e6c9;
color: #2e7d32;
}
.mf-error-message {
background-color: #ffebee;
border: 1px solid #ffcdd2;
color: #c62828;
}
.mf-mx-option {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.3s;
}
.mf-mx-option:hover {
border-color: #0073aa;
background-color: #f0f7fb;
}
.mf-mx-option.selected {
border-color: #0073aa;
background-color: #e6f3fa;
}
.mf-email-list {
margin-top: 20px;
}
.mf-email-row {
display: flex;
margin-bottom: 10px;
gap: 10px;
}
.mf-email-row input {
flex: 1;
}
.mf-add-email-btn {
background-color: #0073aa;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
margin-top: 10px;
}
.mf-add-email-btn:hover {
background-color: #005d87;
}
.mf-remove-email-btn {
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
padding: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.mf-remove-email-btn:hover {
background-color: #d32f2f;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -19,18 +19,15 @@ class RL_MailWarmer_DB_Helper {
$conversation_sql = "CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}" . self::$conversations_table . "` (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
campaign_id BIGINT(20) UNSIGNED NOT NULL,
email_account_id BIGINT(20) UNSIGNED NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
status VARCHAR(20) DEFAULT 'new' NOT NULL,
first_message_timestamp DATETIME DEFAULT NULL,
prompt LONGTEXT DEFAULT NULL,
conversation_steps LONGTEXT DEFAULT NULL,
ai_response LONGTEXT DEFAULT NULL,
PRIMARY KEY (id),
KEY campaign_id_idx (campaign_id),
KEY status_idx (status),
KEY first_message_timestamp_idx (first_message_timestamp),
INDEX email_account_id_idx (email_account_id)
KEY first_message_timestamp_idx (first_message_timestamp)
) $charset_collate;";
// Message table
@ -38,7 +35,6 @@ class RL_MailWarmer_DB_Helper {
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
campaign_id BIGINT(20) UNSIGNED NOT NULL,
conversation_id BIGINT(20) UNSIGNED NOT NULL,
email_account_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,
@ -49,8 +45,7 @@ class RL_MailWarmer_DB_Helper {
PRIMARY KEY (id),
INDEX scheduled_idx (scheduled_for_timestamp, status),
INDEX conversation_id_idx (conversation_id),
INDEX campaign_id_idx (campaign_id),
INDEX email_account_id_idx (email_account_id)
INDEX campaign_id_idx (campaign_id)
) $charset_collate;";
// Backup table
@ -90,37 +85,21 @@ class RL_MailWarmer_DB_Helper {
*/
public static function insert_conversation($conversation_data) {
global $wpdb;
// $wpdb->insert(
// "{$wpdb->prefix}" . self::$conversations_table,
// [
// 'campaign_id' => $campaign_id,
// 'conversation_steps' => json_encode($conversation_steps),
// 'prompt' => $prompt,
// ],
// ['%d', '%s', '%s']
// );
$wpdb->insert("{$wpdb->prefix}" . self::$conversations_table, $conversation_data);
return $wpdb->insert_id;
}
/**
* Update a conversation record.
*
* @param int $conversation_id The ID of the conversation to update.
* @param array $update_data An associative array of columns and values to update.
*
* @return int|false The number of rows updated, or false on error.
*/
public static function update_conversation($conversation_id, $update_data) {
global $wpdb;
// Ensure that $conversation_id is a valid integer
if (!is_int($conversation_id) || $conversation_id <= 0) {
return false;
}
// Update the table with the provided data
$updated = $wpdb->update(
"{$wpdb->prefix}" . self::$conversations_table,
$update_data,
['id' => $conversation_id] // WHERE clause
);
return $updated !== false ? $updated : false;
}
/**
* Insert a message record.
*/
@ -150,99 +129,6 @@ class RL_MailWarmer_DB_Helper {
return $wpdb->insert_id;
}
/**
* Delete all conversations and messages for a given campaign ID.
*
* @param int $campaign_id The ID of the campaign.
*/
public static function delete_all_conversations_messages($campaign_id) {
global $wpdb;
// Ensure campaign_id is an integer
$campaign_id = (int) $campaign_id;
$conversations_table = $wpdb->prefix . self::$conversations_table;
$messages_table = $wpdb->prefix . self::$messages_table;
// Delete messages
$delete_messages_result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM $messages_table WHERE campaign_id = %d",
$campaign_id
)
);
log_to_file("delete_all_conversations_messages - delete_messages_result: ", $delete_messages_result);
// Delete conversations
$delete_conversations_result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM $conversations_table WHERE campaign_id = %d",
$campaign_id
)
);
log_to_file("delete_all_conversations_messages - delete_conversations_result: ", $delete_conversations_result);
}
/**
* Delete all future conversations and messages for a given campaign ID.
*
* @param int $campaign_id The ID of the campaign.
*/
public static function delete_future_conversations_messages($campaign_id) {
global $wpdb;
$conversations_table = $wpdb->prefix . self::$conversations_table;
$messages_table = $wpdb->prefix . self::$messages_table;
$current_time = current_time('mysql');
// Delete future messages
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $messages_table WHERE campaign_id = %d AND scheduled_for_timestamp > %s",
$campaign_id,
$current_time
)
);
// Delete future conversations
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $conversations_table WHERE campaign_id = %d AND first_message_timestamp > %s",
$campaign_id,
$current_time
)
);
}
/**
* Delete all conversations and messages older than X days.
*
* @param int $days The number of days.
*/
public static function delete_old_conversations_messages($days) {
global $wpdb;
$conversations_table = $wpdb->prefix . self::$conversations_table;
$messages_table = $wpdb->prefix . self::$messages_table;
$threshold_date = date('Y-m-d H:i:s', strtotime("-$days days"));
// Delete old messages
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $messages_table WHERE scheduled_for_timestamp < %s",
$threshold_date
)
);
// Delete old conversations
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $conversations_table WHERE first_message_timestamp < %s",
$threshold_date
)
);
}
/**
* Fetch pending messages.

View file

@ -115,15 +115,9 @@ class RL_MailWarmer_Domain_Helper {
}
$domain_name = $domain_post->post_title;
try {
$credentials = self::get_cloudflare_credentials($domain_post);
} catch (Exception $e) {
throw new Exception(__('get_cloudflare_client - Failed to find CloudFlare zone: ', 'rl-mailwarmer') . $e->getMessage());
return false;
}
if ($credentials) {
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.cloudflare.com/client/v4/',
'domain' => $domain_name,
@ -135,10 +129,6 @@ class RL_MailWarmer_Domain_Helper {
],
]);
return $client;
} else {
log_to_file("get_cloudflare_client - Unable to get cloudflare client for $domain_post->post_title");
return false;
}
}
@ -336,14 +326,9 @@ class RL_MailWarmer_Domain_Helper {
$client = self::get_cloudflare_client($domain);
// $zone_id = self::get_cloudflare_zone_id($client, $credentials, $domain_post->post_title);
if ($client) {
$dns_records = self::fetch_dns_records($client);
log_to_file("generate_domain_report - All Records: ", $dns_records);
// log_to_file("generate_domain_report - All Records: ", $dns_records);
if (isset($dns_records[0])) {
$report = [
'domain_health' => self::check_domain_registration($domain_name),
'a_record' => self::check_a_record($dns_records),
@ -353,19 +338,6 @@ class RL_MailWarmer_Domain_Helper {
'dmarc_record' => self::check_dmarc_record($dns_records),
'blacklists' => self::check_blacklists($domain_name),
];
} else {
$report = [
'domain_health' => self::check_domain_registration($domain_name),
'blacklists' => self::check_blacklists($domain_name),
];
}
} else {
log_to_file("generate_domain_report - Unable to connect to CloudFlare for $domain_name");
$report = [
'domain_health' => self::check_domain_registration($domain_name),
'blacklists' => self::check_blacklists($domain_name),
];
}
// log_to_file("generate_domain_report - Health Report for $domain_name: ", $report);
@ -467,13 +439,13 @@ class RL_MailWarmer_Domain_Helper {
*/
private static function check_a_record($dns_records)
{
$domain_name = $dns_records[0]['name'];
$domain_name = $dns_records[0]['zone_name'];
// log_to_file("check_a_record - Running check_mx_record for $domain_name");
foreach ($dns_records as $record) {
// Check if the record matches the criteria
// log_to_file("check_a_record - DNS Record: ", $record);
if ( ($record['name'] === $domain_name) && ($record['type'] === 'A') ) {
if ( ($record['zone_name'] === $record['name']) && ($record['type'] === 'A') ) {
$ip = $record['content'];
$http_status = self::get_http_status($domain_name);
@ -529,12 +501,12 @@ class RL_MailWarmer_Domain_Helper {
*/
private static function check_mx_record($dns_records)
{
$domain_name = $dns_records[0]['name'];
$domain_name = $dns_records[0]['zone_name'];
// log_to_file("check_mx_record - Running check_mx_record for $domain_name");
foreach ($dns_records as $record) {
// Check if the record matches the criteria
if ( ($record['name'] === $domain_name) && ($record['type'] === 'MX') ) {
if ( ($record['zone_name'] === $domain_name) && ($record['type'] === 'MX') ) {
$host = $record['content'];
$ptr_record = gethostbyaddr(gethostbyname($host));
@ -574,7 +546,7 @@ class RL_MailWarmer_Domain_Helper {
log_to_file("update_mx_record - Searching for existing record");
// throw new Exception('Failed to fetch existing DNS records from CloudFlare.');
foreach ($dns_records as $record) {
foreach ($dns_records['result'] as $record) {
if ($record['content'] === $content && $record['priority'] === $priority) {
$existing_record_id = $record['id'];
log_to_file("update_mx_record - Matching record found");
@ -607,14 +579,14 @@ class RL_MailWarmer_Domain_Helper {
log_to_file("update_mx_record - Attempting to update record");
$response = self::update_dns_record($domain_post->ID, $domain_post->post_title, 'MX', $domain_post->post_title, $content, $ttl, $priority);
// $result = json_decode($response->getBody(), true);
$result = json_decode($response->getBody(), true);
log_to_file("update_mx_record - Result: ", $result);
// if (!$result['success']) {
// throw new Exception('Failed to update or create the MX record in CloudFlare.');
// }
if (!$result['success']) {
throw new Exception('Failed to update or create the MX record in CloudFlare.');
}
return $response; // Return the CloudFlare response
return $result; // Return the CloudFlare response
} catch (Exception $e) {
log_to_file('update_mx_record - Error in update_mx_record: ' . $e->getMessage());
return 'Error: ' . $e->getMessage();
@ -630,7 +602,7 @@ class RL_MailWarmer_Domain_Helper {
*/
private static function check_spf_record($dns_records)
{
$domain_name = $dns_records[0]['name'];
$domain_name = $dns_records[0]['zone_name'];
// log_to_file("check_spf_record - Running check_spf_record for $domain_name");
foreach ($dns_records as $record) {
@ -733,7 +705,7 @@ class RL_MailWarmer_Domain_Helper {
*/
private static function check_dmarc_record($dns_records)
{
$domain_name = $dns_records[0]['name'];
$domain_name = $dns_records[0]['zone_name'];
// log_to_file("check_dmarc_record - Running check_dmarc_record for $domain_name");
foreach ($dns_records as $record) {
@ -849,7 +821,8 @@ class RL_MailWarmer_Domain_Helper {
'TXT',
$name,
$wrapped_content,
$existing_record['ttl']
$existing_record['ttl'],
$credentials
);
} else {
return self::update_dns_record(
@ -859,6 +832,7 @@ class RL_MailWarmer_Domain_Helper {
$name,
$wrapped_content,
3600, // Default TTL
$credentials
);
}
}
@ -874,7 +848,7 @@ class RL_MailWarmer_Domain_Helper {
*/
private static function check_dkim_record($dns_records, $selectors = [])
{
$domain_name = $dns_records[0]['name'];
$domain_name = $dns_records[0]['zone_name'];
// log_to_file("check_dkim_record - Running check_dkim_record for $domain_name");
$dkim_records = [];
@ -1098,28 +1072,7 @@ class RL_MailWarmer_Domain_Helper {
}
}
} else {
log_to_file("fix_deliverability_dns_issues - No servers selected for the domain. Choosing system default.");
$server = get_field('defaut_mailferno_mx', 'option');
$server_id = $server->ID;
$selector = get_field('dkim_selector', $server_id);
$value = get_field('dkim_value', $server_id);
if ($selector && $value) {
$dkim_record = $findRecord($dns_records, "{$selector}._domainkey", true);
if (!$dkim_record) {
try {
$dkim_result = self::update_dkim_record($domain_post, $selector, 'add', $value);
$results['dkim'][$selector] = $dkim_result
? "DKIM record for selector '{$selector}' added successfully."
: "Failed to add DKIM record for selector '{$selector}'.";
} catch (Exception $e) {
$results['dkim'][$selector] = 'Error: ' . $e->getMessage();
}
} else {
$results['dkim'][$selector] = "DKIM record for selector '{$selector}' already exists.";
}
} else {
$results['dkim'][$server_id] = 'Missing DKIM selector or value for server: ' . $server_id;
}
$results['dkim'] = 'No servers selected for the domain.';
}
// DMARC

View file

@ -1,637 +0,0 @@
<?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)
{
// log_to_file("check_mail_login - Email account id: {$email_account}");
// Get the post object
$post = is_numeric($email_account) ? get_post($email_account) : $email_account;
if (!$post || $post->post_type !== 'email-account') {
// log_to_file("check_mail_login - Not an email account post-type");
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 - email_provider_id: {$email_provider_id}");
// 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 = [
'email_address' => $post->post_title,
'full_name' => get_post_meta($post->ID, 'full_name', true),
'email_signature' => get_post_meta($post->ID, 'email_signature', true),
'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($settings);
// log_to_file("check_mail_login - IMAP Result for " . $post->post_title . ": ", $imap_result);
$results['IMAP'] = $imap_result ? __('SUCCESS', 'rl-mailwarmer') : $imap_result->get_error_message();
update_post_meta($post->ID, 'imap_status', $results['IMAP']);
}
// Validate SMTP connection if required
if ($protocol === null || strtoupper($protocol) === 'SMTP') {
$smtp_result = self::validate_smtp_connection($settings, true);
// log_to_file("check_mail_login - SMTP Result for " . $post->post_title . ": ", $smtp_result);
$results['SMTP'] = $smtp_result ? __('SUCCESS', 'rl-mailwarmer') : $smtp_result->get_error_message();
update_post_meta($post->ID, 'smtp_status', $results['SMTP']);
}
// 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.
*/
public 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 array $settings The server settings (email_account, imap_server, imap_port, imap_password).
* @return bool True if the connection is successful, false otherwise.
*/
public static function validate_imap_connection($settings)
{
if ( empty($settings['email_address']) || empty($settings['mail_password']) || empty($settings['imap_server']) || empty($settings['imap_port']) ) {
// log_to_file("validate_imap_connection - Incomplete connection information");
return false; // Missing required settings
}
$password = $settings['mail_password'];
$email = $settings['email_address'];
// log_to_file("validate_imap_connection - Checking IMAP connection for {$email} using: ", $settings);
$imap_server = '{' . $settings['imap_server'] . ':' . $settings['imap_port'] . '/imap/ssl}';
// log_to_file("validate_imap_connection - Trying to open stream for {$email} : {$password} @ {$imap_server}");
// Try connecting to the IMAP server
$imap_stream = @imap_open(
$imap_server,
$email,
$password
);
if ($imap_stream) {
// log_to_file("validate_imap_connection - Stream opened. Closing!");
imap_close($imap_stream); // Close connection if successful
return true;
}
// log_to_file("validate_imap_connection - Unable to open stream");
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.
*/
public static function validate_smtp_connection($settings, $send_test_email = false)
{
if ( empty($settings['email_address']) || empty($settings['mail_password']) || empty($settings['smtp_server']) || empty($settings['smtp_port']) ) {
// log_to_file("validate_smtp_connection - Incomplete connection information");
return false; // Missing required settings
}
$email = $settings['email_address'];
$password = $settings['mail_password'];
// log_to_file("validate_smtp_connection - Settings for {$email}: ", $settings);
$signature = str_replace('\n', PHP_EOL, $settings['email_signature']);
$test_to_email = "ruben@redlotusaustin.com";
$email_body = "<p>This is a test email to verify SMTP connection for {$email}\n\n{$signature}</p><br /><br /><span style='font-size:0.5rem;'>MFTID-0000000000000000</span>";
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("{$settings['full_name']} <{$email}>")
->to($test_to_email)
->subject('SMTP Connection Test for ' . $email)
->html($email_body);
if ($send_test_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)
{
$domain_post = get_post($domain);
// 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_post->post_title}";
// Generate a random password
$random_password = wp_generate_password(16, false, false);
$signature = self::generate_random_email_signature($first_name, $last_name, $email_address);
$servers[] = 108;
$mailferno_default_email_provider = 92;
// Create the email-account post
$post_id = wp_insert_post([
'post_type' => 'email-account',
'post_status' => 'publish',
'post_title' => $email_address,
'meta_input' => [
'full_name' => "{$first_name} {$last_name}",
'mail_password' => $random_password,
'email_signature' => $signature,
'domain' => $domain_post->ID,
'include_in_reply_pool' => true,
'include_in_cc_pool' => true,
'include_in_warmup_pool' => true,
'servers' => $servers,
'email_provider' => $mailferno_default_email_provider,
],
]);
// log_to_file("generate_random_accounts - Added email account to local server: $post_id");
if ($post_id && !is_wp_error($post_id)) {
$generated_accounts[] = [
'name' => "{$first_name} {$last_name}",
'email' => $email_address,
'password' => $random_password,
'post_id' => $post_id,
];
// log_to_file("generate_random_accounts - {$first_name} {$last_name}\t{$email_address}\t{$random_password}");
$add_account_result = self::modify_email_account_on_server($post_id, 'create');
// log_to_file("generate_random_accounts - Result of attempting to add account to remote server: ", $add_account_result);
if ( isset($add_account_result['errors']) ) {
// log_to_file("generate_random_accounts - Error modifying account on remote server: ", $add_account_result['errors']);
} else {
// log_to_file("generate_random_accounts - Added $email_address to remote server: ", $add_account_result);
$login_test_results = self::check_mail_login($post_id);
// log_to_file("generate_random_accounts - Login test results: ", $login_test_results);
}
} else {
error_log('Failed to create email-account post: ' . print_r($post_id, true));
}
}
return $generated_accounts;
}
/**
* Generate a randomized email signature.
*
* @param string $first_name The first name of the person.
* @param string $last_name The last name of the person.
* @param string $email_address The email address of the person.
* @return string The generated email signature.
*/
private static function generate_random_email_signature($first_name, $last_name, $email_address) {
// First line variations
$first_line_variations = [
'Best regards',
'Regards',
'Yours truly',
'Sincerely',
'Warm regards',
'Kind regards',
'Best wishes',
'Respectfully',
'With gratitude',
'All the best',
'Cheers',
'Thank you',
'Warm wishes',
'Yours sincerely',
'Cordially',
'With appreciation',
'Many thanks',
'Take care',
'Faithfully',
'Always',
];
// Randomized job titles
$job_titles = [
'Software Engineer',
'Marketing Specialist',
'Sales Manager',
'Project Coordinator',
'Product Designer',
'Customer Success Lead',
];
// Randomized phone formats
// $phone_formats = [
// '(555) %03d-%04d',
// '555-%03d-%04d',
// '+1 555 %03d %04d',
// ];
// // Social media handles (optional)
// $social_links = [
// 'LinkedIn' => 'https://linkedin.com/in/' . strtolower($first_name . $last_name),
// 'Twitter' => 'https://twitter.com/' . strtolower($first_name . $last_name),
// 'GitHub' => 'https://github.com/' . strtolower($first_name . $last_name),
// ];
// Generate random elements
$first_line = $first_line_variations[array_rand($first_line_variations)];
$job_title = $job_titles[array_rand($job_titles)];
// $phone_number = sprintf($phone_formats[array_rand($phone_formats)], rand(100, 999), rand(1000, 9999));
// $selected_social = array_rand($social_links);
// Build the email signature
$signature = "<p>{$first_line},</p>";
$signature .= "<p><strong>{$first_name} {$last_name}</strong><br>";
$signature .= "{$job_title}<br>";
$signature .= "Email: <a href='mailto:{$email_address}'>{$email_address}</a><br>";
// $signature .= "Phone: {$phone_number}<br>";
// $signature .= "<a href='{$social_links[$selected_social]}' target='_blank'>{$selected_social}</a></p>";
return $signature;
}
/**
* 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', true);
if (!$domain_id) {
return new WP_Error('missing_domain', __('No associated domain found.', 'rl-mailwarmer'));
}
$server_ids = get_post_meta($domain_id, '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);
$full_name = get_post_meta($account_id, 'full_name', 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 = "sudo virtualmin";
if ($action === 'create') {
$command .= " create-user --domain $domain --user $username --pass '$password' --real '$full_name'";
} elseif ($action === 'update') {
$command .= " modify-user --domain $domain --user $username --pass '$password' --real '$full_name'";
} elseif ($action === 'delete') {
$command .= " delete-user --domain $domain --user $username";
} else {
return new WP_Error('invalid_action', __('Invalid action specified.', 'rl-mailwarmer'));
}
// log_to_file("modify_email_account_on_server - SSH Command: ", $command);
// 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
}
}
add_action('restrict_manage_posts', function ($post_type) {
if ($post_type === 'email-account') {
?>
<button id="test-connections-button" class="button button-primary" style="margin-left: 10px;">
<?php esc_html_e('Test Selected Connections', 'rl-mailwarmer'); ?>
</button>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('test-connections-button').addEventListener('click', function (event) {
event.preventDefault(); // Prevent form submission
const selectedAccounts = Array.from(document.querySelectorAll('.check-column input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value)
.filter(value => value !== 'on'); // Exclude the "select all" checkbox
if (selectedAccounts.length === 0) {
alert('<?php esc_html_e('Please select at least one account.', 'rl-mailwarmer'); ?>');
return;
}
if (confirm('<?php esc_html_e('Are you sure you want to test connections for the selected accounts?', 'rl-mailwarmer'); ?>')) {
jQuery.post(ajaxurl, {
action: 'rl_test_connections',
account_ids: selectedAccounts
}, function (response) {
alert(response.data.message);
});
}
});
});
</script>
<?php
}
});
add_action('wp_ajax_rl_test_connections', function () {
$account_ids = isset($_POST['account_ids']) ? array_map('intval', $_POST['account_ids']) : [];
if (empty($account_ids)) {
wp_send_json_error(['message' => __('No email accounts selected.', 'rl-mailwarmer')]);
}
foreach ($account_ids as $account_id) {
// Call the check_mail_login function for this email account
$result = RL_MailWarmer_Email_Helper::check_mail_login($account_id);
}
wp_send_json_success(['message' => __('Connections tested successfully for selected accounts.', 'rl-mailwarmer')]);
});

View file

@ -1,534 +0,0 @@
<?php
/**
* Helper functions for the conversation messages
*/
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport\Transport;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use PhpImap\Mailbox;
use phpseclib3\Net\SSH2;
use phpseclib3\Crypt\PublicKeyLoader;
if (!defined('ABSPATH')) {
exit;
}
class RL_MailWarmer_Message_Handler {
/**
* Process pending messages.
*/
public static function process_pending_messages() {
// log_to_file("process_pending_messages - Running");
global $wpdb;
$results = [
'success' => 0,
'failure' => 0,
];
// Fetch the next 100 pending messages with scheduled timestamps in the past
$table_name = $wpdb->prefix . 'rl_mailwarmer_messages';
$messages = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $table_name WHERE status = %s AND scheduled_for_timestamp < %s ORDER BY scheduled_for_timestamp ASC LIMIT 10",
'scheduled',
current_time('mysql')
),
ARRAY_A
);
// if (empty($messages)) {
// // log_to_file("process_pending_messages - messages empty");
// return;
// }
$messages_count = count($messages);
if ($messages_count > 0) {
action_log("process_pending_messages - Processing {$messages_count} messages" );
foreach ($messages as $message) {
log_to_file("==========================================================");
try {
// if (!empty($message['first_message']) && $message['first_message']) {
// log_to_file("process_pending_messages - trying send_message");
// $result = self::send_message($message);
// } else {
// log_to_file("process_pending_messages - trying reply_message");
// $result = self::reply_message($message);
// }
log_to_file("process_pending_messages - trying send_message for {$message['id']} from {$message['from_email']} to ", $message['to_email']);
$result = self::send_message($message);
// Update message status to 'sent' on success
if ($result) {
self::update_message_status($message['id'], 'sent');
$results['success']++;
log_to_file("process_pending_messages - Success sending message: {$message['id']}");
action_log("process_pending_messages - Sent email {$message['id']} from {$message['from_email']} to ", $message['to_email']);
} else {
self::update_message_status($message['id'], 'failed');
log_to_file("process_pending_messages - Error sending message: {$message['id']}");
$results['failure']++;
}
} catch (Exception $e) {
// Handle errors gracefully and log them
log_to_file('process_pending_messages - Error processing message ID ' . $message['id'] . ': ' . $e->getMessage());
self::update_message_status($message['id'], 'failed');
$results['failure']++;
}
// sleep(3);
}
log_to_file("process_pending_messages - Results: ", $results);
action_log("process_pending_messages - Finished processing {$messages_count} messages with {$results['success']} sent and {$results['failure']} failures");
}
return $results;
}
/**
* Send the first message in a conversation.
*
* @param array $message The message details.
* @return bool True if the message is sent successfully, false otherwise.
* @throws Exception If required fields are missing or an error occurs during sending.
*/
public static function send_message($message) {
// log_to_file("send_message - Running");
// log_to_file("send_message - Message: ", $message);
// Prepare email data and connection info
$email_data = self::prepare_email_data($message);
// log_to_file("send_message - Email Data: ", $email_data);
// Extract connection info
$connection_info = $email_data['connection_info'];
if (!empty($connection_info['smtp_password'])) {
$password = $connection_info['smtp_password'];
} else {
$password = $connection_info['mail_password'];
}
// Check required fields
if (empty($email_data['to']) || empty($email_data['from']) || empty($email_data['subject']) || empty($email_data['text_body'])) {
// log_to_file("send_message - Missing required fields for sending the email");
throw new Exception(__('Missing required fields for sending the email.', 'rl-mailwarmer'));
}
// Create the SMTP transport
try {
// log_to_file("send_message - Creating Transport");
// Create the SMTP transport
$transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
$connection_info['smtp_server'],
$connection_info['smtp_port']
);
// Set authentication details
$transport->setUsername($email_data['from']);
$transport->setPassword($password);
$to_addresses = $email_data['to'];
if (!is_array($to_addresses)) {
$to_addresses = json_decode($to_addresses, true);
}
$to_addresses_type = gettype($to_addresses);
// log_to_file("send_message - To ({$to_addresses_type}): ", $to_addresses);
// Create the mailer
$mailer = new Symfony\Component\Mailer\Mailer($transport);
// Send an email
$email_message = (new Symfony\Component\Mime\Email())
->from(new Address($email_data['from'], $email_data['name']))
->to(...$to_addresses)
->subject($email_data['subject'])
->text($email_data['text_body'])
->html($email_data['html_body']);
// Add headers
$campaign_tracking_id = $email_data['campaign_tracking_id'];
// $previous_message_id = $message['previous_message_id'];
// if ($previous_message_id) {
// $campaign_tracking_id .= '-' . $previous_message_id;
// }
$email_message->getHeaders()->addTextHeader('X-MFTID', $campaign_tracking_id);
// log_to_file("send_message - Creating email with MFTID: {$campaign_tracking_id}");
// log_to_file("send_message - Trying to send email.");
$smtp_result = $mailer->send($email_message);
// log_to_file("send_message - Message sent!", $smtp_result);
return true;
} catch (TransportExceptionInterface $e) {
log_to_file("send_message - Error sending email {$message['id']}: " . $e->getMessage());
return false;
}
}
public static function search_email_by_x_mftid($imap_stream, $imap_server, $campaign_tracking_id) {
$folders = imap_list($imap_stream, $imap_server, '*');
$result = ['folder' => null, 'email' => null];
$search_term = '/X\-MFTID: ' . preg_quote($campaign_tracking_id, '/') . '.+/i';
log_to_file("search_email_by_x_mftid - search term: {$search_term}");
foreach ($folders as $folder) {
$decoded_folder = imap_utf7_decode($folder);
// log_to_file("search_email_by_x_mftid - decoded_folder: ", $decoded_folder);
$status = imap_status($imap_stream, $decoded_folder, SA_MESSAGES);
if ($status->messages > 0) {
log_to_file("search_email_by_x_mftid - Searching {$decoded_folder}");
$emails = imap_search($imap_stream, 'TEXT "' . $campaign_tracking_id . '"', SE_UID);
if ($emails) {
$result['folder'] = $decoded_folder;
$result['email'] = $emails;
break;
}
}
}
return $result;
}
/**
* Prepare email data and fetch connection info.
*
* @param array $message The message data.
* @return array Prepared email data and connection info.
* @throws Exception If required data is missing.
*/
private static function prepare_email_data($message) {
// log_to_file("prepare_email_data - Running");
// log_to_file("prepare_email_data - Message: ", $message);
// Ensure 'from' is valid and fetch connection info
if (empty($message['from_email'])) {
throw new Exception(__('Missing "from" address in the message.', 'rl-mailwarmer'));
}
// Find the email-account post matching the 'from' address
$from_post_id = self::find_email_account_by_address($message['from_email']);
if (!$from_post_id) {
throw new Exception(__('No matching email account found for "from" address.', 'rl-mailwarmer'));
}
// Fetch connection details
// log_to_file("prepare_email_data - Getting connection info");
$full_name = get_post_meta($from_post_id, 'full_name', true);
$mail_password = get_post_meta($from_post_id, 'mail_password', true);
$email_provider_id = get_post_meta($from_post_id, 'email_provider', true);
$connection_info = RL_MailWarmer_Email_Helper::get_provider_defaults($email_provider_id);
$connection_info['username'] = $message['from_email'];
$connection_info['mail_password'] = $mail_password;
// log_to_file("prepare_email_data - Connection Info: ", $connection_info);
// Override provider defaults with account-specific settings
foreach (['smtp_server', 'smtp_port', 'smtp_password', 'imap_server', 'imap_port', 'imap_password'] as $key) {
$meta_value = get_post_meta($from_post_id, $key, true);
if (!empty($meta_value)) {
$connection_info[$key] = $meta_value;
}
}
// Handle recipients
// log_to_file("prepare_email_data - Handling recipients");
// $to_emails = json_decode();
$to_emails = is_array($message['to_email']) ? $message['to_email'] : json_decode($message['to_email']);
$cc_emails = is_array($message['cc']) ? $message['cc'] : json_decode($message['cc']);
$campaign_tracking_id = $message['campaign_tracking_id'] . '-' . $message['id'];
$previous_message_id = $message['previous_message_id'];
$text_body = $message['body'] . "\n\n" . $campaign_tracking_id;
$html_body = $message['body'] . "<br /><br /><span style='font-size:0.5rem;'>{$campaign_tracking_id}</span>";
return [
'id' => $message['id'],
'connection_info' => $connection_info,
'to' => array_filter(array_map('trim', array_map('stripslashes', $to_emails))),
'cc' => array_filter(array_map('trim', array_map('stripslashes', $cc_emails))),
'subject' => $message['subject'],
'text_body' => $text_body,
'html_body' => $html_body,
'from' => $message['from_email'],
'name' => $full_name,
'campaign_tracking_id' => $campaign_tracking_id,
'previous_message_id' => $previous_message_id,
];
}
/**
* Find the email-account post ID by email address.
*
* @param string $email_address The email address to search for.
* @return int|null The post ID if found, or null.
*/
private static function find_email_account_by_address($email_address) {
// log_to_file("find_email_account_by_address - Searching for: $email_address");
$query = new WP_Query([
'post_type' => 'email-account',
'post_status' => 'publish',
'title' => $email_address,
'fields' => 'ids',
]);
if (!empty($query->posts)) {
return $query->posts[0];
} else {
return new WP_Error('find_email_account_by_address - Unable to find a matching email address');
}
}
/**
* Update the status of a message.
*
* @param int $message_id The ID of the message to update.
* @param string $status The new status ('completed', 'failed', etc.).
* @return void
*/
private static function update_message_status($message_id, $status) {
global $wpdb;
$table_name = $wpdb->prefix . 'rl_mailwarmer_messages';
$wpdb->update(
$table_name,
['status' => $status],
['id' => $message_id],
['%s'],
['%d']
);
}
// /**
// * Reply to an email message.
// *
// * @param array $message The message details.
// * @return bool True if the message is replied to successfully, false otherwise.
// * @throws Exception If required fields are missing or an error occurs.
// */
// public static function reply_message($message) {
// // Prepare email data and connection info
// $email_data = self::prepare_email_data($message);
// log_to_file("reply_message - Email Data: ", $email_data);
// // Validate required fields
// if (empty($email_data['to']) || empty($email_data['from']) || empty($email_data['subject']) || empty($email_data['text_body'])) {
// throw new Exception(__('Missing required fields for replying to the email.', 'rl-mailwarmer'));
// }
// // Extract connection info
// $connection_info = $email_data['connection_info'];
// $emails = '';
// try {
// // log_to_file("reply_message - Trying to reply via IMAP {$connection_info['imap_server']}:{$connection_info['imap_port']}");
// // $imap = new \PhpImap\Mailbox(
// // sprintf('{%s:%s/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']),
// // $connection_info['username'],
// // $connection_info['mail_password'],
// // null,
// // 'UTF-8'
// // );
// // $imap->checkMailbox();
// // // Search for the email with the matching subject
// // log_to_file("reply_message - Searching for message with X-MFTID");
// // $emails = $imap->searchMailbox('HEADER X-MFTID "' . addslashes($email_data['campaign_tracking_id']) . '"');
// $password = $connection_info['mail_password'];
// $email = $connection_info['username'];
// log_to_file("reply_message - Trying to reply via IMAP for {$email} using: ", $connection_info);
// $imap_server = '{' . $connection_info['imap_server'] . ':' . $connection_info['imap_port'] . '/imap/ssl}';
// log_to_file("reply_message - Trying to open stream for {$email} : {$password} @ {$imap_server}");
// // Try connecting to the IMAP server
// $imap_stream = @imap_open(
// $imap_server,
// $email,
// $password
// );
// if ($imap_stream) {
// log_to_file("reply_message - IMAP stream opened.");
// $imap_search_result = self::search_email_by_x_mftid($imap_stream, $imap_server, $email_data['campaign_tracking_id']);
// if ($imap_search_result['folder']) {
// log_to_file("reply_message - Email with X-MFTID found in folder: {$imap_search_result['folder']}");
// log_to_file("reply_message - Email: ", $imap_search_result['email']);
// // $emails = $imap_search_result['email'];
// // Continue with reply logic
// } else {
// log_to_file("No email found with X-MFTID: " . $email_data['campaign_tracking_id']);
// }
// imap_close($imap_stream);
// }
// if (!empty($emails)) {
// // Fetch the email data
// $original_email = $imap->getMail($emails[0]);
// log_to_file("reply_message - Message found!");
// // log_to_file("reply_message - IMAP Message: ", $original_email);
// // Step 2: Send the reply via SMTP
// $transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
// $connection_info['smtp_server'],
// $connection_info['smtp_port']
// );
// // Set authentication details
// $transport->setUsername($connection_info['username']);
// $transport->setPassword($connection_info['mail_password']);
// $to_addresses = $email_data['to'];
// if (!is_array($to_addresses)) {
// $to_addresses = json_decode($to_addresses, true);
// }
// $mailer = new Mailer($transport);
// $reply_email = (new Email())
// ->from(new Address($email_data['from'], $email_data['name']))
// ->to(...$to_addresses)
// ->subject('Re: ' . $original_email->subject)
// ->text($email_data['text_body'])
// ->html($email_data['html_body']);
// // Add headers
// $campaign_tracking_id = $email_data['campaign_tracking_id'];
// $reply_email->getHeaders()->addTextHeader('X-MFTID', $campaign_tracking_id);
// // Add headers for threading
// $headers = $reply_email->getHeaders();
// $headers->addTextHeader('In-Reply-To', $original_email->messageId);
// $headers->addTextHeader('References', trim($original_email->headers->references . ' ' . $original_email->messageId));
// // ->addHeader('In-Reply-To', $original_email->messageId)
// // ->addHeader('References', trim($original_email->headers->references . ' ' . $original_email->messageId));
// // if (!empty($email_data['cc'])) {
// // $reply_email->cc(...$email_data['cc']);
// // }
// $mailer->send($reply_email);
// log_to_file("reply_message - Successfully sent IMAP/SMTP reply from ", $email_data['from']);
// // Step 3: Upload the reply to the Sent folder
// $imap_stream = imap_open(
// sprintf('{%s:%d/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']),
// $connection_info['username'],
// $connection_info['mail_password']
// );
// $raw_message = $reply_email->toString(); // Convert the Email object to raw MIME format
// imap_append($imap_stream, sprintf('{%s}/Sent', $connection_info['imap_server']), $raw_message);
// imap_close($imap_stream);
// // Create the reply headers
// // $reply_headers = [
// // 'In-Reply-To' => $original_email->messageId,
// // 'References' => trim($original_email->headers->references . ' ' . $original_email->messageId),
// // ];
// // // Construct the reply body
// // $reply_body = $email_data['body'] . "\n\n" .
// // 'On ' . $original_email->date . ', ' . $original_email->fromName . ' <' . $original_email->fromAddress . '> wrote:' . "\n" .
// // $original_email->textPlain;
// // // Send the reply via IMAP
// // log_to_file("reply_message - Sending message via IMAP");
// // $imap->addMessageToSentFolder(
// // 'To: ' . implode(', ', $email_data['to']) . "\r\n" .
// // 'Cc: ' . implode(', ', $email_data['cc']) . "\r\n" .
// // 'Subject: Re: ' . $original_email->subject . "\r\n" .
// // 'From: ' . $email_data['from'] . "\r\n" .
// // 'In-Reply-To: ' . $reply_headers['In-Reply-To'] . "\r\n" .
// // 'References: ' . $reply_headers['References'] . "\r\n" .
// // "\r\n" .
// // $reply_body
// // );
// // log_to_file("reply_message - Done message via IMAP");
// // $mailer = new Mailer($transport);
// // $mailer->send($reply);
// return true; // Reply sent successfully
// } else {
// log_to_file("reply_message - Unable to reply via IMAP. Falling back to SMTP");
// }
// } catch (Exception $e) {
// log_to_file('reply_message - IMAP Error: ' . $e->getMessage());
// }
// // Fallback to SMTP if IMAP fails
// try {
// log_to_file("reply_message - Falling back to SMTP");
// $result = self::send_message($message);
// return $result;
// // $to_addresses = $email_data['to'];
// // if (!is_array($to_addresses)) {
// // $to_addresses = json_decode($to_addresses, true);
// // }
// // log_to_file("reply_message - Creating SMTP message");
// // $smtp_reply = (new Email())
// // ->from(new Address($email_data['from'], $email_data['name']))
// // ->to(...$to_addresses)
// // ->subject($email_data['subject'])
// // ->text($email_data['text_body'])
// // ->html($email_data['html_body']);
// // // Add headers
// // $campaign_tracking_id = $email_data['campaign_tracking_id'];
// // $smtp_reply->getHeaders()->addTextHeader('X-MFTID', $campaign_tracking_id);
// // // Add CCs if present
// // // if (!empty($email_data['cc'])) {
// // // $smtp_reply->cc(...$email_data['cc']);
// // // }
// // // Create the SMTP transport
// // log_to_file("reply_message - Creating SMTP transport");
// // $transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
// // $connection_info['smtp_server'],
// // $connection_info['smtp_port']
// // );
// // // Set authentication details
// // $transport->setUsername($connection_info['username']);
// // $transport->setPassword($connection_info['mail_password']);
// // // Create the mailer
// // log_to_file("reply_message - Creating SMTP mailer");
// // $mailer = new Symfony\Component\Mailer\Mailer($transport);
// // $smtp_result = $mailer->send($smtp_reply);
// // log_to_file("reply_message - Sent reply via fallback SMTP from {$email_data['from']}:", $smtp_result);
// // // log_to_file('reply_message - SMTP Send Success (?)');
// // Fallback SMTP reply sent successfully
// } catch (Exception $e) {
// log_to_file('reply_message - SMTP Error: ' . $e->getMessage());
// return false; // Reply failed
// }
// }
}

View file

@ -1,247 +0,0 @@
<?php
add_action('admin_menu', function () {
add_menu_page(
__('Messages', 'rl-mailwarmer'), // Page title
__('Messages', 'rl-mailwarmer'), // Menu title
'manage_options', // Capability
'rl-mailwarmer-messages', // Menu slug
'rl_mailwarmer_render_messages_page', // Callback function
'dashicons-email-alt', // Icon
9 // Position
);
});
/**
* Render the Messages admin page with checkboxes and a Delete button.
*/
function rl_mailwarmer_render_messages_page() {
if (!current_user_can('manage_options')) {
return;
}
global $wpdb;
$conversation_table = $wpdb->prefix . 'rl_mailwarmer_conversations';
$message_table = $wpdb->prefix . 'rl_mailwarmer_messages';
// Fetch conversations with their messages
$conversations = $wpdb->get_results("
SELECT c.id as conversation_id, c.campaign_id, c.created_at, m.id as message_id, m.scheduled_for_timestamp, m.from_email, m.to_email, m.subject
FROM $conversation_table c
LEFT JOIN $message_table m ON c.id = m.conversation_id
WHERE m.status != 'scheduled'
ORDER BY c.created_at DESC, m.scheduled_for_timestamp ASC
");
?>
<div class="wrap">
<h1><?php esc_html_e('Messages', 'rl-mailwarmer'); ?></h1>
<?php if (empty($conversations)) : ?>
<p><?php esc_html_e('No messages found.', 'rl-mailwarmer'); ?></p>
<?php else : ?>
<form id="message-management-form" method="post">
<table class="widefat fixed striped">
<thead>
<tr>
<th style="width: 50px;">
<input type="checkbox" id="check-all-messages">
</th>
<th><?php esc_html_e('Conversation ID', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Campaign ID', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Created At', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Message ID', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Scheduled For', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('From', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('To', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Subject', 'rl-mailwarmer'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($conversations as $conversation) : ?>
<tr>
<td>
<?php if ($conversation->message_id) : ?>
<input type="checkbox" name="selected_messages[]" value="<?php echo esc_attr($conversation->message_id); ?>">
<?php endif; ?>
</td>
<td><?php echo esc_html($conversation->conversation_id); ?></td>
<td><?php echo esc_html($conversation->campaign_id); ?></td>
<td><?php echo esc_html($conversation->created_at); ?></td>
<td><?php echo esc_html($conversation->message_id); ?></td>
<td><?php echo esc_html($conversation->scheduled_for_timestamp); ?></td>
<td><?php echo esc_html($conversation->from_email); ?></td>
<td><?php echo esc_html($conversation->to_email); ?></td>
<td><?php echo esc_html($conversation->subject); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p>
<button type="button" id="delete-selected-messages" class="button button-primary">
<?php esc_html_e('Delete Items', 'rl-mailwarmer'); ?>
</button>
</p>
</form>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Select all messages
const checkAll = document.getElementById('check-all-messages');
const checkboxes = document.querySelectorAll('input[name="selected_messages[]"]');
checkAll.addEventListener('change', function () {
checkboxes.forEach(checkbox => checkbox.checked = checkAll.checked);
});
// Handle delete button click
document.getElementById('delete-selected-messages').addEventListener('click', function () {
const selectedIds = Array.from(checkboxes)
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value);
if (selectedIds.length === 0) {
alert('<?php esc_html_e('Please select at least one message to delete.', 'rl-mailwarmer'); ?>');
return;
}
if (confirm('<?php esc_html_e('Are you sure you want to delete the selected messages?', 'rl-mailwarmer'); ?>')) {
jQuery.post(ajaxurl, {
action: 'rl_delete_messages',
message_ids: selectedIds,
}, function (response) {
if (response.success) {
alert('<?php esc_html_e('Selected messages deleted successfully.', 'rl-mailwarmer'); ?>');
location.reload(); // Reload the page to update the list
} else {
alert('<?php esc_html_e('Failed to delete messages.', 'rl-mailwarmer'); ?>');
}
});
}
});
});
</script>
<?php
}
add_action('wp_ajax_rl_delete_messages', function () {
global $wpdb;
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]);
}
$message_ids = isset($_POST['message_ids']) ? array_map('intval', $_POST['message_ids']) : [];
if (empty($message_ids)) {
wp_send_json_error(['message' => __('No messages selected.', 'rl-mailwarmer')]);
}
$table_name = $wpdb->prefix . 'rl_mailwarmer_messages';
// Delete selected messages
$placeholders = implode(',', array_fill(0, count($message_ids), '%d'));
$query = "DELETE FROM $table_name WHERE id IN ($placeholders)";
$result = $wpdb->query($wpdb->prepare($query, $message_ids));
if ($result === false) {
wp_send_json_error(['message' => __('Failed to delete messages.', 'rl-mailwarmer')]);
}
wp_send_json_success(['message' => __('Messages deleted successfully.', 'rl-mailwarmer')]);
});
/**
* Add a metabox to the WP dashboard for monitoring and processing the message queue.
*/
add_action('wp_dashboard_setup', function () {
wp_add_dashboard_widget(
'rl_mailwarmer_message_queue',
__('Message Queue', 'rl-mailwarmer'),
'rl_mailwarmer_render_message_queue_widget'
);
});
/**
* Render the Message Queue dashboard widget.
*/
function rl_mailwarmer_render_message_queue_widget() {
global $wpdb;
// Count past-due messages
$table_name = $wpdb->prefix . 'rl_mailwarmer_messages';
$past_due_count = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM $table_name WHERE status = %s AND scheduled_for_timestamp < %s",
'scheduled',
current_time('mysql')
)
);
?>
<div id="rl-mailwarmer-queue">
<p><strong><?php esc_html_e('Past-Due Messages:', 'rl-mailwarmer'); ?></strong> <?php echo esc_html($past_due_count); ?></p>
<button id="process-message-queue" class="button button-primary">
<?php esc_html_e('Process Message Queue', 'rl-mailwarmer'); ?>
</button>
<div id="message-queue-result" style="margin-top: 10px;"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const button = document.getElementById('process-message-queue');
const resultDiv = document.getElementById('message-queue-result');
button.addEventListener('click', function () {
button.disabled = true;
resultDiv.textContent = '<?php esc_html_e('Processing...', 'rl-mailwarmer'); ?>';
jQuery.post(ajaxurl, {
action: 'rl_mailwarmer_process_message_queue',
security: '<?php echo esc_js(wp_create_nonce('rl_mailwarmer_message_queue_nonce')); ?>'
}, function (response) {
button.disabled = false;
if (response.success) {
console.log("Success");
resultDiv.textContent = '<?php esc_html_e('Messages processed:', 'rl-mailwarmer'); ?> ' + response.data.success + ' with ' + response.data.failure + ' failures' ;
} else {
console.log("Failure");
resultDiv.textContent = '<?php esc_html_e('Error:', 'rl-mailwarmer'); ?> ' + response.data.message;
}
});
});
});
</script>
<?php
}
/**
* AJAX handler to process the message queue.
*/
add_action('wp_ajax_rl_mailwarmer_process_message_queue', function () {
// log_to_file("wp_ajax_rl_mailwarmer_process_message_queue - Running");
// Verify nonce
if (!check_ajax_referer('rl_mailwarmer_message_queue_nonce', 'security', false)) {
wp_send_json_error(['message' => __('Invalid nonce.', 'rl-mailwarmer')]);
}
// Ensure the user has permission
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]);
}
try {
// Process pending messages
// log_to_file("wp_ajax_rl_mailwarmer_process_message_queue - Trying process_pending_messages()");
$results = RL_MailWarmer_Message_Handler::process_pending_messages();
wp_send_json_success($results);
} catch (Exception $e) {
wp_send_json_error(['message' => $e->getMessage()]);
}
});

View file

@ -1,143 +0,0 @@
<?php
class PostTypeList {
private string $post_type;
private array $labels;
private int $items_per_page;
private int $paged;
private array $additional_query_args;
public function __construct(string $post_type, array $labels = [], array $additional_query_args = []) {
$this->post_type = $post_type;
$this->items_per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 25;
$this->paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
// Base query args
$base_args = [
'orderby' => 'title',
'order' => 'ASC',
'post_type' => $this->post_type,
'posts_per_page' => $this->items_per_page,
'paged' => $this->paged,
];
// Add owner_id filter only for non-administrators
if (!current_user_can('administrator')) {
$base_args['meta_query'] = array(
array(
'key' => 'owner_id',
'value' => get_current_user_id(),
'compare' => '='
)
);
}
// Merge with any additional query args
$this->additional_query_args = array_merge($base_args, $additional_query_args);
// Default labels that can be overridden
$this->labels = array_merge([
'title' => ucfirst($post_type) . 's',
'no_items' => 'No ' . strtolower($post_type) . 's found.',
'delete_confirm' => 'Are you sure you want to delete this ' . strtolower($post_type) . '?'
], $labels);
}
public function render(): void {
$query = new WP_Query($this->additional_query_args);
$edit_url_base = "/dashboard/" . $this->post_type . "s/edit-" . $this->post_type . "?edit=";
?>
<div class="wrap">
<!-- <h1><?php //echo esc_html($this->labels['title']); ?></h1> -->
<!-- Items list -->
<div class="post-type-list">
<?php if ($query->have_posts()) : ?>
<?php
while ($query->have_posts()) : $query->the_post();
$post_id = get_the_ID();
?>
<div class="post-type-item">
<div class="post-type-content">
<a href="<?php echo get_permalink(); ?>" class="post-type-name">
<?php the_title(); ?>
</a>
<div class="row-actions">
<span class="edit">
<a href="<?php echo $edit_url_base . $post_id; ?>">Edit</a> |
</span>
<span class="trash">
<?php echo $this->get_delete_link($post_id); ?>
</span>
</div>
</div>
</div>
<?php endwhile; ?>
<?php else : ?>
<p><?php echo $this->labels['no_items']; ?></p>
<?php endif; ?>
</div>
<div class="page-nav">
<div class="tablenav-pages">
<?php
echo paginate_links(array(
'base' => add_query_arg('paged', '%#%'),
'format' => '',
'prev_text' => __('&laquo;'),
'next_text' => __('&raquo;'),
'total' => $query->max_num_pages,
'current' => $this->paged
));
?>
</div>
</div>
<?php wp_reset_postdata(); ?>
</div>
<script>
function handlePerPageChange(select) {
// Remove the paged parameter if it exists in the URL
var form = document.getElementById('items-per-page-form');
var currentPath = window.location.pathname;
// Remove any existing page/X/ from the path
currentPath = currentPath.replace(/page\/\d+\/?/, '');
// Update form action to use the cleaned path
form.action = currentPath;
// console.log(currentPath);
// Submit the form
form.submit();
}
</script>
<?php
}
protected function get_delete_link($post_id) {
if (!can_delete_post($post_id)) {
return '';
}
$current_url = add_query_arg(array()); // Gets current URL with existing parameters
$delete_url = wp_nonce_url(
add_query_arg(
array(
'action' => 'delete',
'post' => $post_id,
'redirect_to' => urlencode($current_url)
),
$current_url // Use current page URL instead of admin URL
),
'delete-post_' . $post_id
);
return sprintf(
'<a href="%s" onclick="return confirm(\'Are you sure you want to delete this item?\');" class="delete">Delete</a>',
esc_url($delete_url)
);
}
}

View file

@ -13,8 +13,8 @@ class RL_MailWarmer_Scheduler {
add_filter('cron_schedules', [__CLASS__, 'add_cron_timings']);
// Call local functions to call the remote classes, so we can debug cron jobs
add_action('rl_mailwarmer_process_messages', [__CLASS__, 'cron_process_pending_messages']);
add_action('rl_mailwarmer_process_upcoming_conversations', [__CLASS__, 'cron_process_upcoming_conversations']);
add_action('rl_mailwarmer_process_messages', [__CLASS__, 'process_pending_messages']);
add_action('rl_mailwarmer_process_upcoming_conversations', [__CLASS__, 'process_upcoming_conversations']);
// add_action('rl_mailwarmer_process_messages', [RL_MailWarmer_Message_Handler::class, 'process_pending_messages']);
// add_action('rl_mailwarmer_process_upcoming_conversations', [RL_MailWarmer_Conversation_Handler::class, 'process_upcoming_conversations']);
@ -79,16 +79,16 @@ class RL_MailWarmer_Scheduler {
/**
* Process pending messages by delegating to the Message Handler.
*/
public static function cron_process_pending_messages() {
// action_log("====================== Running Cron to process messages ========================");
RL_MailWarmer_Message_Handler::process_pending_messages();
public static function process_pending_messages() {
// log_to_file("schedule_cron_jobs ====================== Running Cron to process messages ========================");
// RL_MailWarmer_Message_Handler::process_pending_messages();
}
/**
* Process pending conversations by delegating to the Message Handler.
*/
public static function cron_process_upcoming_conversations() {
// action_log("====================== Running Cron to process conversations ========================");
RL_MailWarmer_Conversation_Handler::process_upcoming_conversations();
public static function process_upcoming_conversations() {
// log_to_file("schedule_cron_jobs ====================== Running Cron to process conversations ========================");
// RL_MailWarmer_Message_Handler::process_upcoming_conversations();
}
}

View file

@ -1,138 +1,5 @@
<?php
/**
* AJAX endpoints for domain, campaign, and email account management
*/
// DOMAIN MANAGEMENT AJAX ENDPOINTS
/**
* Create or update a domain via AJAX
*/
add_action('wp_ajax_rl_mailwarmer_save_domain', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'domain_form_nonce')) {
wp_send_json_error(__('Security check failed', 'rl-mailwarmer'));
}
$current_user_id = get_current_user_id();
$post_title = strtolower(sanitize_text_field($_POST['domain_name']));
$cloudflare_email = isset($_POST['cloudflare_api_email']) ? sanitize_email($_POST['cloudflare_api_email']) : '';
$cloudflare_key = isset($_POST['cloudflare_api_key']) ? sanitize_text_field($_POST['cloudflare_api_key']) : '';
$domain_in_use = isset($_POST['domain_in_use']) ? $_POST['domain_in_use'] : '';
$update_dns = isset($_POST['update_dns']) ? $_POST['update_dns'] : '';
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
$new_post = ($post_id == 0);
// Check for existing domain with same name for new posts
if ($new_post) {
$args = [
'post_type' => 'domain',
'post_status' => 'any',
'title' => $post_title,
'fields' => 'ids',
'posts_per_page' => 1
];
$query = new WP_Query($args);
if ($query->have_posts()) {
wp_send_json_error(['message' => __('Domain already exists.', 'rl-mailwarmer')]);
return;
}
}
// Prepare post data
$post_data = array(
'post_title' => $post_title,
'post_type' => 'domain',
'post_status' => 'publish'
);
if ($post_id) {
$post_data['ID'] = $post_id;
$post_id = wp_update_post($post_data);
} else {
$post_id = wp_insert_post($post_data);
}
if (is_wp_error($post_id)) {
wp_send_json_error(['message' => $post_id->get_error_message()]);
return;
}
// Update meta fields
update_post_meta($post_id, 'cloudflare_api_email', $cloudflare_email);
update_post_meta($post_id, 'cloudflare_api_key', $cloudflare_key);
update_post_meta($post_id, 'domain_in_use', $domain_in_use);
if ($new_post) {
// Set owner to current user for new domains
update_post_meta($post_id, 'owner_id', $current_user_id);
// Run domain health check for new domains
RL_MailWarmer_Domain_Helper::save_domain_health_report($post_id);
}
// Handle DNS updates if requested
if ($update_dns) {
$results = RL_MailWarmer_Domain_Helper::fix_deliverability_dns_issues($post_id);
}
// Handle MX records based on domain usage
if (!$domain_in_use) {
$server = get_field('defaut_mailferno_mx', 'option');
$update_MX_result = RL_MailWarmer_Domain_Helper::update_mx_record($post_id, $server->post_title, 0, $ttl = 3600);
}
// Return success response with post ID and permalink
wp_send_json_success([
'message' => $new_post ? __('Domain added successfully.', 'rl-mailwarmer') : __('Domain updated successfully.', 'rl-mailwarmer'),
'post_id' => $post_id,
'permalink' => get_permalink($post_id)
]);
});
/**
* Delete a domain via AJAX
*/
add_action('wp_ajax_rl_mailwarmer_delete_domain', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'domain_delete_nonce')) {
wp_send_json_error(__('Security check failed', 'rl-mailwarmer'));
}
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
if (!$post_id) {
wp_send_json_error(__('Invalid domain ID', 'rl-mailwarmer'));
return;
}
// Verify post type and user permissions
$post = get_post($post_id);
if (!$post || $post->post_type !== 'domain') {
wp_send_json_error(__('Invalid domain', 'rl-mailwarmer'));
return;
}
// Verify ownership
$owner_id = get_post_meta($post_id, 'owner_id', true);
if ($owner_id != get_current_user_id() && !current_user_can('manage_options')) {
wp_send_json_error(__('Permission denied', 'rl-mailwarmer'));
return;
}
// Delete the domain
$result = wp_delete_post($post_id, true);
if (!$result) {
wp_send_json_error(__('Failed to delete domain', 'rl-mailwarmer'));
return;
}
wp_send_json_success(['message' => __('Domain deleted successfully', 'rl-mailwarmer')]);
});
add_action('wp_ajax_rl_mailwarmer_check_domain_health', function () {
// log_to_file("wp_ajax_rl_mailwarmer_check_domain_health called");
// Verify nonce
@ -356,304 +223,6 @@ add_action('wp_ajax_rl_mailwarmer_create_dns_backup', function () {
}
});
// CAMPAIGN MANAGEMENT AJAX ENDPOINTS
/**
* Create or update a campaign via AJAX
*/
add_action('wp_ajax_rl_mailwarmer_save_campaign', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'campaign_form_nonce')) {
wp_send_json_error(__('Security check failed', 'rl-mailwarmer'));
}
$current_user_id = get_current_user_id();
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
$new_post = ($post_id == 0);
$calculate_timeline = isset($_POST['calculate_timeline']) ? true : false;
$target_profession = false;
$target_profession_other = false;
// Prepare post data
$post_data = array(
'post_title' => sanitize_text_field($_POST['campaign_name']),
'post_type' => 'campaign',
'post_status' => 'publish'
);
if ($post_id) {
$post_data['ID'] = $post_id;
$post_id = wp_update_post($post_data);
} else {
$post_id = wp_insert_post($post_data);
// Generate tracking ID for new campaigns
RL_MailWarmer_Campaign_Helper::generate_campaign_tracking_id($post_id);
}
if (is_wp_error($post_id)) {
wp_send_json_error(['message' => $post_id->get_error_message()]);
return;
}
// Update campaign enabled status
$campaign_enabled = isset($_POST['enabled']) ? true : false;
update_post_meta($post_id, 'enabled', $campaign_enabled);
// Update ACF fields
$acf_fields = [
'domain_id',
'email_accounts',
'start_date',
'warmup_period',
'starting_volume',
'target_volume',
'weekend_reduction_factor',
'target_profession',
'target_profession_other',
'campaign_conversation_topics'
];
foreach ($acf_fields as $field) {
if (isset($_POST[$field])) {
switch ($field) {
case 'email_accounts':
$campaign_email_accounts = $_POST[$field];
$is_limited = false;
// Check if any email account has limited access
foreach ($campaign_email_accounts as $account_id) {
$limited_access = get_post_meta($account_id, 'limited_access', true);
if ($limited_access === true || $limited_access === '1') {
$is_limited = true;
break;
}
}
// Update campaign_limited flag
update_post_meta($post_id, 'campaign_limited', $is_limited);
update_field($field, $campaign_email_accounts, $post_id);
break;
case 'domain_id':
try {
update_field('domain', $_POST['domain_id'], $post_id);
} catch (Exception $e) {
wp_send_json_error(['message' => $e->getMessage()]);
return;
}
break;
case 'target_profession':
$target_profession = $_POST[$field];
break;
case 'target_profession_other':
$target_profession_other = $_POST[$field];
break;
default:
update_field($field, sanitize_text_field($_POST[$field]), $post_id);
break;
}
}
}
if ($target_profession_other) {
$target_profession = $target_profession_other;
}
// If no target profession is set, choose one from the default profession pool.
if ( ! $target_profession ) {
$professions = rl_get_textarea_meta_as_array('option', 'default_profession_pool');
if ( ! empty($professions) ) {
// Assign a random profession from the list
$target_profession = $professions[array_rand($professions)];
}
}
// Set campaign owner
update_post_meta($post_id, 'owner_id', $current_user_id);
// Calculate timeline if needed
if ($new_post || $calculate_timeline) {
RL_MailWarmer_DB_Helper::delete_all_conversations_messages($post_id);
RL_MailWarmer_Campaign_Helper::calculate_campaign_timeline($post_id);
}
// Return success response
wp_send_json_success([
'message' => $new_post ? __('Campaign added successfully.', 'rl-mailwarmer') : __('Campaign updated successfully.', 'rl-mailwarmer'),
'post_id' => $post_id,
'permalink' => get_permalink($post_id)
]);
});
/**
* Delete a campaign via AJAX
*/
add_action('wp_ajax_rl_mailwarmer_delete_campaign', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'campaign_delete_nonce')) {
wp_send_json_error(__('Security check failed', 'rl-mailwarmer'));
}
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
if (!$post_id) {
wp_send_json_error(__('Invalid campaign ID', 'rl-mailwarmer'));
return;
}
// Verify post type
$post = get_post($post_id);
if (!$post || $post->post_type !== 'campaign') {
wp_send_json_error(__('Invalid campaign', 'rl-mailwarmer'));
return;
}
// Verify ownership
$owner_id = get_post_meta($post_id, 'owner_id', true);
if ($owner_id != get_current_user_id() && !current_user_can('manage_options')) {
wp_send_json_error(__('Permission denied', 'rl-mailwarmer'));
return;
}
// Delete campaign messages and timeline first
RL_MailWarmer_DB_Helper::delete_all_conversations_messages($post_id);
// Delete the campaign
$result = wp_delete_post($post_id, true);
if (!$result) {
wp_send_json_error(__('Failed to delete campaign', 'rl-mailwarmer'));
return;
}
wp_send_json_success(['message' => __('Campaign deleted successfully', 'rl-mailwarmer')]);
});
// EMAIL ACCOUNT MANAGEMENT AJAX ENDPOINTS
/**
* Create or update an email account via AJAX
*/
add_action('wp_ajax_rl_mailwarmer_save_email_account', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'email_form_nonce')) {
wp_send_json_error(__('Security check failed', 'rl-mailwarmer'));
}
$current_user_id = get_current_user_id();
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
$post_title = strtolower(sanitize_email($_POST['email_address']));
$new_post = ($post_id == 0);
// Check for existing email account with same address for new posts
if ($new_post) {
$args = [
'post_type' => 'email-account',
'post_status' => 'any',
'title' => $post_title,
'fields' => 'ids',
'posts_per_page' => 1
];
$query = new WP_Query($args);
if ($query->have_posts()) {
wp_send_json_error(['message' => __('Email account already exists.', 'rl-mailwarmer')]);
return;
}
}
// Prepare post data
$post_data = array(
'post_title' => $post_title,
'post_type' => 'email-account',
'post_status' => 'publish'
);
if ($post_id) {
$post_data['ID'] = $post_id;
$post_id = wp_update_post($post_data);
} else {
$post_id = wp_insert_post($post_data);
}
if (is_wp_error($post_id)) {
wp_send_json_error(['message' => $post_id->get_error_message()]);
return;
}
// Update meta fields
$meta_fields = [
'domain_id', 'mail_password', 'full_name', 'email_signature',
'email_provider', 'smtp_password', 'smtp_server', 'smtp_port',
'imap_password', 'imap_server', 'imap_port'
];
foreach ($meta_fields as $field) {
if (isset($_POST[$field])) {
update_post_meta($post_id, $field, sanitize_text_field($_POST[$field]));
}
}
// For new accounts, set ownership and include in warmup pool
if ($new_post) {
update_post_meta($post_id, 'owner_id', $current_user_id);
update_post_meta($post_id, 'include_in_warmup_pool', $current_user_id);
}
// Return success response
wp_send_json_success([
'message' => $new_post ? __('Email account added successfully.', 'rl-mailwarmer') : __('Email account updated successfully.', 'rl-mailwarmer'),
'post_id' => $post_id,
'permalink' => get_permalink($post_id)
]);
});
/**
* Delete an email account via AJAX
*/
add_action('wp_ajax_rl_mailwarmer_delete_email_account', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'email_delete_nonce')) {
wp_send_json_error(__('Security check failed', 'rl-mailwarmer'));
}
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
if (!$post_id) {
wp_send_json_error(__('Invalid email account ID', 'rl-mailwarmer'));
return;
}
// Verify post type
$post = get_post($post_id);
if (!$post || $post->post_type !== 'email-account') {
wp_send_json_error(__('Invalid email account', 'rl-mailwarmer'));
return;
}
// Verify ownership
$owner_id = get_post_meta($post_id, 'owner_id', true);
if ($owner_id != get_current_user_id() && !current_user_can('manage_options')) {
wp_send_json_error(__('Permission denied', 'rl-mailwarmer'));
return;
}
// Delete the email account
$result = wp_delete_post($post_id, true);
if (!$result) {
wp_send_json_error(__('Failed to delete email account', 'rl-mailwarmer'));
return;
}
wp_send_json_success(['message' => __('Email account deleted successfully', 'rl-mailwarmer')]);
});
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')) {
@ -702,226 +271,3 @@ add_action('wp_ajax_rl_mailwarmer_check_mail_login', function () {
wp_send_json_error($e->getMessage());
}
});
/**
* Dashboard Modal AJAX handlers
*/
/**
* Validate domain for adding to Mailferno
*/
add_action('wp_ajax_validate_domain', function() {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'dashboard_modal_nonce')) {
wp_send_json_error(['message' => __('Security check failed', 'rl-mailwarmer')]);
}
// Get and validate domain
$domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
if (empty($domain)) {
wp_send_json_error(['message' => __('Domain name is required', 'rl-mailwarmer')]);
}
// Check if domain already exists
$existing_domain = get_page_by_title($domain, OBJECT, 'domain');
if ($existing_domain) {
wp_send_json_error(['message' => __('Domain already exists in your account', 'rl-mailwarmer')]);
return;
}
// Check if domain is valid (DNS resolves)
if (!checkdnsrr($domain, 'A') && !checkdnsrr($domain, 'AAAA')) {
wp_send_json_error(['message' => __('Domain does not resolve to a valid IP address', 'rl-mailwarmer')]);
return;
}
// Check if domain uses CloudFlare
$uses_cloudflare = false;
// Get the NS records for the domain
$ns_records = dns_get_record($domain, DNS_NS);
// If no NS records found directly, try with www
if (empty($ns_records)) {
$ns_records = dns_get_record('www.' . $domain, DNS_NS);
}
// Check if any NS record contains cloudflare
if (!empty($ns_records)) {
foreach ($ns_records as $record) {
if (isset($record['target']) && stripos($record['target'], 'cloudflare') !== false) {
$uses_cloudflare = true;
break;
}
}
}
wp_send_json_success([
'isValid' => true,
'usesCloudFlare' => $uses_cloudflare
]);
});
/**
* Verify CloudFlare credentials and check MX records
*/
add_action('wp_ajax_verify_cloudflare', function() {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'dashboard_modal_nonce')) {
wp_send_json_error(['message' => __('Security check failed', 'rl-mailwarmer')]);
}
$domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
$email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
$key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
if (empty($domain) || empty($email) || empty($key)) {
wp_send_json_error(['message' => __('Missing required fields', 'rl-mailwarmer')]);
return;
}
// Use existing cloudflare helper to verify credentials and get zone ID
try {
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.cloudflare.com/client/v4/',
'headers' => [
'Content-Type' => 'application/json',
],
]);
$response = $client->get('zones', [
'headers' => [
'Authorization' => "Bearer {$key}",
'Content-Type' => 'application/json',
],
'query' => ['name' => $domain],
]);
$data = json_decode($response->getBody()->getContents(), true);
if (!isset($data['result'][0]['id'])) {
wp_send_json_error(['message' => __('Domain not found in your CloudFlare account', 'rl-mailwarmer')]);
return;
}
$zone_id = $data['result'][0]['id'];
// Check if MX records exist
$has_mx = false;
// Query CloudFlare for MX records
$mx_response = $client->get("zones/{$zone_id}/dns_records", [
'headers' => [
'Authorization' => "Bearer {$key}",
'Content-Type' => 'application/json',
],
'query' => ['type' => 'MX'],
]);
$mx_data = json_decode($mx_response->getBody()->getContents(), true);
if (isset($mx_data['result']) && !empty($mx_data['result'])) {
$has_mx = true;
}
wp_send_json_success([
'zoneId' => $zone_id,
'hasMX' => $has_mx
]);
} catch (Exception $e) {
wp_send_json_error(['message' => __('Failed to verify CloudFlare credentials: ', 'rl-mailwarmer') . $e->getMessage()]);
}
});
/**
* Create domain with all configurations
*/
add_action('wp_ajax_create_domain', function() {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'dashboard_modal_nonce')) {
wp_send_json_error(['message' => __('Security check failed', 'rl-mailwarmer')]);
}
// Get domain data
$domain_data_json = isset($_POST['domainData']) ? $_POST['domainData'] : '';
if (empty($domain_data_json)) {
wp_send_json_error(['message' => __('No domain data provided', 'rl-mailwarmer')]);
return;
}
$domain_data = json_decode(stripslashes($domain_data_json), true);
// Create the domain post
$current_user_id = get_current_user_id();
$domain_name = $domain_data['domain'];
$post_data = array(
'post_title' => $domain_name,
'post_type' => 'domain',
'post_status' => 'publish'
);
$post_id = wp_insert_post($post_data);
if (is_wp_error($post_id)) {
wp_send_json_error(['message' => $post_id->get_error_message()]);
return;
}
// Save domain meta data
update_post_meta($post_id, 'cloudflare_api_email', $domain_data['cloudflareEmail']);
update_post_meta($post_id, 'cloudflare_api_key', $domain_data['cloudflareKey']);
update_post_meta($post_id, 'cloudflare_zone_id', $domain_data['cloudflareZoneId']);
update_post_meta($post_id, 'domain_in_use', !$domain_data['configureMailferno']);
update_post_meta($post_id, 'owner_id', $current_user_id);
// Run a domain health check
$domain_report_id = RL_MailWarmer_Domain_Helper::save_domain_health_report($post_id);
// Handle DNS configuration if requested
if ($domain_data['configureMailferno']) {
$server = get_field('defaut_mailferno_mx', 'option');
RL_MailWarmer_Domain_Helper::update_mx_record($post_id, $server->post_title, 0, 3600);
}
// Fix DNS records
$results = RL_MailWarmer_Domain_Helper::fix_deliverability_dns_issues($post_id);
// Create email accounts if provided
$email_accounts = [];
if (!empty($domain_data['emailAccounts'])) {
foreach ($domain_data['emailAccounts'] as $email) {
// Extract username from email
$username = explode('@', $email)[0];
// Create email account
$email_post_data = array(
'post_title' => $email,
'post_type' => 'email-account',
'post_status' => 'publish'
);
$email_post_id = wp_insert_post($email_post_data);
if (!is_wp_error($email_post_id)) {
// Set default password and metadata
update_post_meta($email_post_id, 'domain_id', $post_id);
update_post_meta($email_post_id, 'mail_password', wp_generate_password(12, false));
update_post_meta($email_post_id, 'full_name', ucwords(str_replace(['.', '-', '_'], ' ', $username)));
update_post_meta($email_post_id, 'owner_id', $current_user_id);
update_post_meta($email_post_id, 'include_in_warmup_pool', true);
$email_accounts[] = $email_post_id;
}
}
}
wp_send_json_success([
'message' => __('Domain added successfully. ' . (!empty($email_accounts) ? count($email_accounts) . ' email accounts created.' : ''), 'rl-mailwarmer'),
'domain_id' => $post_id,
'email_accounts' => $email_accounts
]);
});

View file

@ -12,7 +12,7 @@
* Set the CUSTOM_DEBUG_LOG file in wp-config.php
*
*/
function log_to_file($message, $data = null, $enable_backtrace = false) {
function log_to_file($message, $data = false){
if ($message) {
$log_File = CUSTOM_DEBUG_LOG;
@ -21,48 +21,13 @@ function log_to_file($message, $data = null, $enable_backtrace = false) {
// Convert arrays and objects to JSON format
if (is_array($data) || is_object($data)) {
$data = json_encode($data, JSON_PRETTY_PRINT);
$data = json_encode($data);
$message = $message . "\r\n" . $data;
} else if ($data) {
$message = $message . " " . $data;
}
// Include backtrace information if enabled
if ($enable_backtrace) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$callingFunction = $backtrace[1]['function']; // Access the calling function name
$backtrace_info = json_encode($backtrace, JSON_PRETTY_PRINT);
$message .= "\r\nBacktrace:\r\n" . $backtrace_info;
}
error_log("[$date] " . $message . "\r\n", 3, $log_File);
}
}
function action_log($message, $data = null, $enable_backtrace = false) {
if ($message) {
$log_File = ACTION_LOG;
$date = new DateTime('now', new DateTimeZone('America/Chicago'));
$date = $date->format("Y/m/d h:i:s");
// Convert arrays and objects to JSON format
if (is_array($data) || is_object($data)) {
$data = json_encode($data, JSON_PRETTY_PRINT);
$message = $message . "\r\n" . $data;
} else if ($data) {
$message = $message . " " . $data;
}
// Include backtrace information if enabled
if ($enable_backtrace) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$callingFunction = $backtrace[1]['function']; // Access the calling function name
$backtrace_info = json_encode($backtrace, JSON_PRETTY_PRINT);
$message .= "\r\nBacktrace:\r\n" . $backtrace_info;
}
error_log("[$date] " . $message . "\r\n", 3, $log_File);
error_log("[$date] " . $message ."\r\n",3,$log_File);
}
}

View file

@ -1,185 +0,0 @@
<?php
add_action('admin_menu', function () {
add_menu_page(
__('Import Data', 'rl-mailwarmer'), // Page title
__('Import Data', 'rl-mailwarmer'), // Menu title
'manage_options', // Capability
'rl-mailwarmer-import-data', // Menu slug
'rl_mailwarmer_render_import_page', // Callback function
'dashicons-upload', // Icon
25 // Position
);
});
/**
* Render the Import Data admin page.
*/
function rl_mailwarmer_render_import_page() {
if (!current_user_can('manage_options')) {
return;
}
// Check for a nonce and handle file upload
if (isset($_POST['rl_import_nonce']) && wp_verify_nonce($_POST['rl_import_nonce'], 'rl_import_data')) {
if (!empty($_FILES['domain_csv']['tmp_name'])) {
rl_mailwarmer_process_domain_csv($_FILES['domain_csv']['tmp_name']);
}
if (!empty($_FILES['email_account_csv']['tmp_name'])) {
rl_mailwarmer_process_email_account_csv($_FILES['email_account_csv']['tmp_name']);
}
}
?>
<div class="wrap">
<h1><?php esc_html_e('Import Data', 'rl-mailwarmer'); ?></h1>
<form method="post" enctype="multipart/form-data">
<?php wp_nonce_field('rl_import_data', 'rl_import_nonce'); ?>
<h2><?php esc_html_e('Import Domains', 'rl-mailwarmer'); ?></h2>
<p><?php esc_html_e('Upload a CSV file to import domains.', 'rl-mailwarmer'); ?></p>
<input type="file" name="domain_csv" accept=".csv" />
<h2><?php esc_html_e('Import Email Accounts', 'rl-mailwarmer'); ?></h2>
<p><?php esc_html_e('Upload a CSV file to import email accounts.', 'rl-mailwarmer'); ?></p>
<input type="file" name="email_account_csv" accept=".csv" />
<p><button type="submit" class="button button-primary"><?php esc_html_e('Upload Files', 'rl-mailwarmer'); ?></button></p>
</form>
</div>
<?php
}
function rl_mailwarmer_process_domain_csv($file_path) {
$handle = fopen($file_path, 'r');
if (!$handle) {
echo '<div class="error"><p>' . __('Failed to open the domain CSV file.', 'rl-mailwarmer') . '</p></div>';
return;
}
// Read the headers
$headers = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
$data = array_combine($headers, $row);
if (empty($data['name'])) {
continue; // Skip invalid rows
}
// Create or update the domain post
$existing_domain = get_posts([
'post_type' => 'domain',
'title' => $data['name'],
'posts_per_page' => 1,
'fields' => 'ids',
]);
$post_id = $existing_domain[0] ?? null;
$post_data = [
'post_title' => $data['name'],
'post_type' => 'domain',
'post_status' => 'publish',
];
if ($post_id) {
$post_data['ID'] = $post_id;
}
$post_id = wp_insert_post($post_data);
if (!is_wp_error($post_id)) {
update_post_meta($post_id, 'cloudflare_api_email', sanitize_text_field($data['cloudflare_api_email']));
update_post_meta($post_id, 'cloudflare_api_key', sanitize_text_field($data['cloudflare_api_key']));
log_to_file("rl_mailwarmer_process_domain_csv - Imported domain: " . $data['name']);
} else {
log_to_file("rl_mailwarmer_process_domain_csv - ERROR importing domain: " . $data['name']);
}
}
fclose($handle);
echo '<div class="updated"><p>' . __('Domains imported successfully.', 'rl-mailwarmer') . '</p></div>';
}
function rl_mailwarmer_process_email_account_csv($file_path) {
$handle = fopen($file_path, 'r');
if (!$handle) {
echo '<div class="error"><p>' . __('Failed to open the email account CSV file.', 'rl-mailwarmer') . '</p></div>';
return;
}
// Read the headers
$headers = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== false) {
$data = array_combine($headers, $row);
if (empty($data['Email Address']) || empty($data['Password'])) {
continue; // Skip invalid rows
}
if (!empty($data['Name'])) {
$name = sanitize_text_field($data['Name']);
}
if (!empty($data['Signature'])) {
$signature = sanitize_textarea_field(wp_slash($data['Signature']));
// log_to_file("rl_mailwarmer_process_email_account_csv - Signature: {$signature}");
}
// Match the email account to a domain
$domain_name = strtolower(substr(strrchr($data['Email Address'], "@"), 1));
$domain = get_posts([
'post_type' => 'domain',
'title' => $domain_name,
'post_status' => 'publish',
'posts_per_page' => 1,
'fields' => 'ids',
]);
// Match the email provider by name
$email_provider = $data['Email Provider'];
// Check if this is a scrubber account
if (!empty($data['Is Scrubber'])) {
$include_in_scrubber_pool = true;
} else {
$include_in_scrubber_pool = false;
}
// Create or update the email account post
$post_data = [
'post_title' => strtolower($data['Email Address']),
'post_type' => 'email-account',
'post_status' => 'publish',
'meta_input' => [
'full_name' => $name,
'mail_password' => sanitize_text_field($data['Password']),
'email_provider' => $email_provider,
'email_signature' => $signature,
'include_in_scrubber_pool' => $include_in_scrubber_pool,
],
];
if (!empty($domain)) {
$post_data['meta_input']['domain'] = $domain[0];
}
$post_id = wp_insert_post($post_data);
if (!is_wp_error($post_id)) {
// $email_account_connection_status = RL_MailWarmer_Email_Helper::check_mail_login($post_id);
log_to_file("rl_mailwarmer_process_email_account_csv - Imported email {$data['Email Address']}: ");
} else {
log_to_file("rl_mailwarmer_process_email_account_csv - ERROR importing email: " . $data['Email Address']);
}
}
fclose($handle);
echo '<div class="updated"><p>' . __('Email accounts imported successfully.', 'rl-mailwarmer') . '</p></div>';
}

View file

@ -1,38 +0,0 @@
jQuery(document).ready(function ($) {
$('#update-mx-record-button').on('click', function (e) {
e.preventDefault();
// const postId = $('#post_ID').val();
const postId = rlMailWarmerMx.post_id;
const action = $('#mx_action').val();
const content = $('#mx_content').val();
const priority = $('#mx_priority').val();
const ttl = $('#mx_ttl').val();
$('#mx-update-result').html('<p>Updating MX record...</p>');
$.ajax({
url: rlMailWarmerMx.ajax_url,
method: 'POST',
data: {
action: 'rl_mailwarmer_update_mx_record',
post_id: postId,
action_type: action,
content: content,
priority: priority,
ttl: ttl,
security: rlMailWarmerMx.nonce,
},
success: function (response) {
if (response.success) {
$('#mx-update-result').html('<p>' + response.data + '</p>');
} else {
$('#mx-update-result').html('<p>Error: ' + response.data + '</p>');
}
},
error: function (xhr, status, error) {
$('#mx-update-result').html('<p>AJAX Error: ' + error + '</p>');
},
});
});
});

View file

@ -1,195 +0,0 @@
/**
* Campaign form AJAX handling
*/
jQuery(document).ready(function($) {
// Handle campaign form submission
$('#campaign-form').on('submit', function(e) {
e.preventDefault();
// Show loading state
const $submitButton = $(this).find('input[type="submit"]');
const originalText = $submitButton.val();
$submitButton.val('Processing...').prop('disabled', true);
// Get form data
const formData = new FormData(this);
formData.append('action', 'rl_mailwarmer_save_campaign');
formData.append('security', campaign_form_vars.nonce);
// Process checkbox fields
formData.set('enabled', $('#enabled').is(':checked') ? '1' : '');
formData.set('calculate_timeline', $('#calculate_timeline').is(':checked') ? '1' : '');
// AJAX request
$.ajax({
url: campaign_form_vars.ajax_url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response.success) {
// Display success message
$('#form-messages')
.removeClass('notice-error')
.addClass('notice notice-success is-dismissible')
.html('<p>' + response.data.message + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
// Reset form or redirect if not staying on page
if (!$('#stay_on_page').is(':checked') && response.data.permalink) {
// window.location.href = response.data.permalink;
return;
} else if (!$('#stay_on_page').is(':checked')) {
window.location.href = campaign_form_vars.campaigns_page;
return;
}
// Reset form for new submission if staying on page
if (!$('#post_id').val()) {
$('#campaign-form')[0].reset();
}
} else {
// Display error message
$('#form-messages')
.removeClass('notice-success')
.addClass('notice notice-error is-dismissible')
.html('<p>' + response.data.message + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
}
},
error: function(xhr, status, error) {
// Display error message
$('#form-messages')
.removeClass('notice-success')
.addClass('notice notice-error is-dismissible')
.html('<p>An error occurred: ' + error + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
},
complete: function() {
// Restore button state
$submitButton.val(originalText).prop('disabled', false);
}
});
});
// Handle delete campaign button click
$('.delete-campaign-btn').on('click', function(e) {
e.preventDefault();
if (!confirm('Are you sure you want to delete this campaign? This action cannot be undone.')) {
return;
}
const postId = $(this).data('id');
// Show loading state
$(this).text('Deleting...').prop('disabled', true);
// AJAX request to delete campaign
$.ajax({
url: campaign_form_vars.ajax_url,
type: 'POST',
data: {
action: 'rl_mailwarmer_delete_campaign',
security: campaign_form_vars.delete_nonce,
post_id: postId
},
success: function(response) {
if (response.success) {
// Redirect to campaigns list page
window.location.href = campaign_form_vars.campaigns_page;
} else {
alert(response.data.message || 'Error deleting campaign');
}
},
error: function() {
alert('An error occurred while deleting the campaign');
}
});
});
/**
* Filter email accounts to only display those matching the selected domain
*/
const $domainSelect = $('#domain_id');
const $emailSelect = $('#email_accounts');
const $emailOptions = $emailSelect.find('option');
function filterEmailAccounts() {
const selectedDomain = $domainSelect.val();
$emailOptions.hide();
if (selectedDomain) {
$emailOptions.each(function() {
const $option = $(this);
const email = $option.text();
const domain = $domainSelect.find('option:selected').text();
if (email.endsWith('@' + domain)) {
$option.show();
}
});
}
}
$domainSelect.on('change', function() {
$emailSelect.val([]); // Clear selection
filterEmailAccounts();
});
// Initial filter
filterEmailAccounts();
/**
* Show the Other Profession field when the Target Profession is set to "Other"
*/
const $professionSelect = $('#target_profession');
const $otherField = $('#target_profession_other').closest('tr');
function toggleOtherField() {
if ($professionSelect.val() === 'Other') {
$otherField.show();
} else {
$otherField.hide();
$('#target_profession_other').val('');
}
}
$professionSelect.on('change', toggleOtherField);
// Initial state
toggleOtherField();
// Helper function to add dismiss button to notices
function addNoticeDismissButton() {
$('.notice.is-dismissible').each(function() {
const $notice = $(this);
const $button = $('<button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>');
// Only add the button if it doesn't exist already
if ($notice.find('.notice-dismiss').length === 0) {
$notice.append($button);
// Handle the dismiss button click
$button.on('click', function() {
$notice.slideUp('fast', function() {
$notice.remove();
});
});
}
});
}
// Initialize dismiss buttons
addNoticeDismissButton();
});

View file

@ -1,484 +0,0 @@
/**
* Dashboard Modal & Multi-step Form Handlers
*/
jQuery(document).ready(function($) {
// Modal elements
const $fabButton = $('.mf-floating-action-button');
const $modalOverlay = $('.mf-modal-overlay');
const $modalContainer = $('.mf-modal-container');
const $modalClose = $('.mf-modal-close');
const $backBtn = $('.mf-back-btn');
const $nextBtn = $('.mf-next-btn');
// Step indicators
const $stepDots = $('.mf-step-dot');
let $steps = $('.mf-step');
// Selection buttons
const $selectionButtons = $('.mf-selection-button');
// Current step and selected type
let currentStep = 0;
let selectedType = '';
let domainData = {
domain: '',
isValid: false,
usesCloudFlare: false,
cloudflareZoneId: '',
cloudflareEmail: '',
cloudflareKey: '',
hasMX: false,
configureMailferno: false,
emailAccounts: []
};
// Open Modal
$fabButton.on('click', function() {
resetForm();
$modalOverlay.addClass('active');
});
// Close Modal
$modalClose.on('click', closeModal);
$modalOverlay.on('click', function(e) {
if (e.target === this) {
closeModal();
}
});
function closeModal() {
$modalOverlay.removeClass('active');
resetForm();
}
// Reset Form
function resetForm() {
currentStep = 0;
selectedType = '';
domainData = {
domain: '',
isValid: false,
usesCloudFlare: false,
cloudflareZoneId: '',
cloudflareEmail: '',
cloudflareKey: '',
hasMX: false,
configureMailferno: false,
emailAccounts: []
};
$selectionButtons.removeClass('selected');
updateStepIndicators();
showCurrentStep();
$('.mf-validation-error').hide();
$('.mf-success-message, .mf-error-message').remove();
$('.mf-loading').hide();
$('#mf-domain-input').val('');
$('#mf-cloudflare-email').val('');
$('#mf-cloudflare-key').val('');
$('.mf-mx-option').removeClass('selected');
$('.mf-email-row:not(:first)').remove();
$('.mf-email-row input').val('');
}
// Selection buttons
$selectionButtons.on('click', function() {
$selectionButtons.removeClass('selected');
$(this).addClass('selected');
selectedType = $(this).data('type');
$nextBtn.removeClass('disabled');
});
// Navigation buttons
$backBtn.on('click', function() {
if (currentStep > 0) {
currentStep--;
updateStepIndicators();
showCurrentStep();
}
});
$nextBtn.on('click', function() {
if ($(this).hasClass('disabled')) return;
// Handle first step (selection)
if (currentStep === 0) {
if (!selectedType) {
alert('Please select an option to continue');
return;
}
// Set up next steps based on selection
setupStepsByType(selectedType);
currentStep++;
updateStepIndicators();
showCurrentStep();
return;
}
// Domain specific step handling
if (selectedType === 'domain') {
const result = handleDomainStep(currentStep);
if (!result) return; // If step validation fails, don't proceed
}
// If we reach here, move to next step
currentStep++;
updateStepIndicators();
showCurrentStep();
});
// Update step indicators
function updateStepIndicators() {
$stepDots.removeClass('active completed');
// Update for current setup
$stepDots.each(function(index) {
if (index < currentStep) {
$(this).addClass('completed');
} else if (index === currentStep) {
$(this).addClass('active');
}
});
// Update back button visibility
if (currentStep === 0) {
$backBtn.hide();
} else {
$backBtn.show();
}
// Update next button text
const isLastStep = (currentStep === $steps.length - 1);
$nextBtn.text(isLastStep ? 'Next' : 'Next');
// When on selection step, disable next until selection made
if (currentStep === 0) {
$nextBtn.toggleClass('disabled', !selectedType);
} else {
$nextBtn.removeClass('disabled');
}
}
// Show current step
function showCurrentStep() {
$steps.removeClass('active');
$steps.eq(currentStep).addClass('active');
}
// Setup steps by selected type
function setupStepsByType(type) {
// Hide all steps except the first one
$steps.not(':first').remove();
// Clear step indicators
$('.mf-steps-indicator').empty();
let stepCount = 1; // Selection step
if (type === 'domain') {
// Step 1: Input domain
$('<div class="mf-step">')
.append('<h3>Enter Domain Name</h3>')
.append('<div class="mf-form-field"><label for="mf-domain-input">Domain Name</label><input type="text" id="mf-domain-input" placeholder="example.com"><div class="mf-validation-error" id="domain-validation-error">Please enter a valid domain name</div></div>')
.append('<div class="mf-loading" style="display:none;"><div class="mf-loading-spinner"></div><p>Validating domain...</p></div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Step 2: CloudFlare verification
$('<div class="mf-step">')
.append('<h3>Verify CloudFlare Connection</h3>')
.append('<div class="mf-form-field"><label for="mf-cloudflare-email">CloudFlare Email</label><input type="email" id="mf-cloudflare-email" placeholder="your@email.com"></div>')
.append('<div class="mf-form-field"><label for="mf-cloudflare-key">CloudFlare API Key</label><input type="password" id="mf-cloudflare-key" placeholder="Your API key"></div>')
.append('<div class="mf-validation-error" id="cloudflare-validation-error">Please enter valid CloudFlare credentials</div>')
.append('<div class="mf-loading" style="display:none;"><div class="mf-loading-spinner"></div><p>Verifying CloudFlare credentials...</p></div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Step 3: MX Configuration
$('<div class="mf-step">')
.append('<h3>Email Server Configuration</h3>')
.append('<p>How would you like to configure email for this domain?</p>')
.append('<div class="mf-mx-options"><div class="mf-mx-option" data-option="mailferno"><h4>Use Mailferno MX Server</h4><p>Configure this domain to use Mailferno\'s email server. Best if this is a new domain not currently used for email.</p></div><div class="mf-mx-option" data-option="existing"><h4>Keep Existing Configuration</h4><p>Keep your current email server configuration. Best if this domain is already being used for email.</p></div></div>')
.append('<div class="mf-validation-error" id="mx-validation-error">Please select an MX configuration option</div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Step 4: Email accounts
$('<div class="mf-step">')
.append('<h3>Add Email Accounts</h3>')
.append('<p>Would you like to create email accounts for this domain now?</p>')
.append('<div class="mf-email-list"><div class="mf-email-row"><input type="text" class="mf-email-local" placeholder="username"><span>@'+domainData.domain+'</span><button type="button" class="mf-remove-email-btn">×</button></div></div>')
.append('<button type="button" class="mf-add-email-btn">+ Add Another Email</button>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Final step
$('<div class="mf-step">')
.append('<h3>Domain Setup Complete</h3>')
.append('<div class="mf-success-message">Your domain has been successfully added to Mailferno!</div>')
.append('<p>You can now continue to manage your domain, email accounts, and campaigns from the dashboard.</p>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
} else if (type === 'campaign') {
// Add campaign specific steps
$('<div class="mf-step">')
.append('<h3>Create Campaign</h3>')
.append('<p>Campaign creation functionality will be implemented here.</p>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Final step
$('<div class="mf-step">')
.append('<h3>Campaign Created</h3>')
.append('<div class="mf-success-message">Your campaign has been successfully created!</div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
} else if (type === 'email') {
// Add email account specific steps
$('<div class="mf-step">')
.append('<h3>Create Email Account</h3>')
.append('<p>Email account creation functionality will be implemented here.</p>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Final step
$('<div class="mf-step">')
.append('<h3>Email Account Created</h3>')
.append('<div class="mf-success-message">Your email account has been successfully created!</div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
}
// Create step indicators
for (let i = 0; i < stepCount; i++) {
$('<div class="mf-step-dot">')
.addClass(i === 0 ? 'completed' : (i === 1 ? 'active' : ''))
.appendTo($('.mf-steps-indicator'));
}
// Make steps accessible
$steps = $('.mf-step');
// Setup event handlers for the domain steps
if (type === 'domain') {
setupDomainStepHandlers();
}
}
// Domain step specific handlers
function setupDomainStepHandlers() {
// Step 1: Domain validation
$('#mf-domain-input').on('input', function() {
// Basic validation
const domain = $(this).val().trim();
const isValid = /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain);
if (isValid) {
$('#domain-validation-error').hide();
domainData.domain = domain;
} else {
$('#domain-validation-error').show();
}
});
// Step 3: MX Configuration
$('.mf-mx-option').on('click', function() {
$('.mf-mx-option').removeClass('selected');
$(this).addClass('selected');
domainData.configureMailferno = ($(this).data('option') === 'mailferno');
});
// Step 4: Email accounts
$('.mf-add-email-btn').on('click', function() {
const $newRow = $('.mf-email-row').first().clone();
$newRow.find('input').val('');
$newRow.insertBefore($(this));
});
// Remove email button
$(document).on('click', '.mf-remove-email-btn', function() {
if ($('.mf-email-row').length > 1) {
$(this).closest('.mf-email-row').remove();
} else {
$('.mf-email-row').find('input').val('');
}
});
}
// Domain step validation and AJAX handling
function handleDomainStep(step) {
// Adjust step to match domain-specific numbering (after selection)
const domainStep = step - 1;
switch(domainStep) {
case 0: // Domain input validation
const domain = $('#mf-domain-input').val().trim();
if (!domain || !/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain)) {
$('#domain-validation-error').show();
return false;
}
domainData.domain = domain;
// Show loading indicator
$steps.eq(currentStep).find('.mf-loading').show();
$nextBtn.addClass('disabled');
// AJAX validation call to check if domain is valid and uses CloudFlare
$.ajax({
url: dashboard_modal_vars.ajax_url,
type: 'POST',
data: {
action: 'validate_domain',
security: dashboard_modal_vars.nonce,
domain: domain
},
success: function(response) {
$steps.eq(currentStep).find('.mf-loading').hide();
if (response.success) {
domainData.isValid = true;
domainData.usesCloudFlare = response.data.usesCloudFlare;
if (!domainData.usesCloudFlare) {
$steps.eq(currentStep).append('<div class="mf-error-message">This domain is not using CloudFlare nameservers. Please configure CloudFlare for this domain before continuing.</div>');
return;
}
// Update domain text in email step
$('.mf-email-row span').text('@' + domain);
// Continue to next step
$nextBtn.removeClass('disabled');
$nextBtn.trigger('click');
} else {
$steps.eq(currentStep).append('<div class="mf-error-message">' + response.data.message + '</div>');
$nextBtn.removeClass('disabled');
}
},
error: function() {
$steps.eq(currentStep).find('.mf-loading').hide();
$steps.eq(currentStep).append('<div class="mf-error-message">Error validating domain. Please try again.</div>');
$nextBtn.removeClass('disabled');
}
});
return false; // Prevent automatic next step - we'll handle it in AJAX callback
case 1: // CloudFlare verification
const email = $('#mf-cloudflare-email').val().trim();
const key = $('#mf-cloudflare-key').val().trim();
if (!email || !key) {
$('#cloudflare-validation-error').show();
return false;
}
domainData.cloudflareEmail = email;
domainData.cloudflareKey = key;
// Show loading indicator
$steps.eq(currentStep).find('.mf-loading').show();
$nextBtn.addClass('disabled');
// AJAX call to verify CloudFlare credentials
$.ajax({
url: dashboard_modal_vars.ajax_url,
type: 'POST',
data: {
action: 'verify_cloudflare',
security: dashboard_modal_vars.nonce,
domain: domainData.domain,
email: email,
key: key
},
success: function(response) {
$steps.eq(currentStep).find('.mf-loading').hide();
if (response.success) {
domainData.cloudflareZoneId = response.data.zoneId;
domainData.hasMX = response.data.hasMX;
// Continue to next step
$nextBtn.removeClass('disabled');
$nextBtn.trigger('click');
} else {
$steps.eq(currentStep).append('<div class="mf-error-message">' + response.data.message + '</div>');
$nextBtn.removeClass('disabled');
}
},
error: function() {
$steps.eq(currentStep).find('.mf-loading').hide();
$steps.eq(currentStep).append('<div class="mf-error-message">Error verifying CloudFlare credentials. Please try again.</div>');
$nextBtn.removeClass('disabled');
}
});
return false; // Prevent automatic next step - we'll handle it in AJAX callback
case 2: // MX Configuration
if (!$('.mf-mx-option.selected').length) {
$('#mx-validation-error').show();
return false;
}
domainData.configureMailferno = ($('.mf-mx-option.selected').data('option') === 'mailferno');
return true;
case 3: // Email accounts
domainData.emailAccounts = [];
let allValid = true;
$('.mf-email-row').each(function() {
const username = $(this).find('.mf-email-local').val().trim();
if (username) {
domainData.emailAccounts.push(username + '@' + domainData.domain);
}
});
if (allValid) {
// Create domain via AJAX
$nextBtn.addClass('disabled');
$.ajax({
url: dashboard_modal_vars.ajax_url,
type: 'POST',
data: {
action: 'create_domain',
security: dashboard_modal_vars.nonce,
domainData: JSON.stringify(domainData)
},
success: function(response) {
if (response.success) {
// Update final step
$steps.eq(currentStep + 1).find('.mf-success-message').text(response.data.message);
// Continue to next step
$nextBtn.removeClass('disabled');
$nextBtn.trigger('click');
// Reload page after a short delay to show updated dashboard
setTimeout(function() {
window.location.reload();
}, 5000);
} else {
$steps.eq(currentStep).append('<div class="mf-error-message">' + response.data.message + '</div>');
$nextBtn.removeClass('disabled');
}
},
error: function() {
$steps.eq(currentStep).append('<div class="mf-error-message">Error creating domain. Please try again.</div>');
$nextBtn.removeClass('disabled');
}
});
return false;
}
return allValid;
case 4: // Final step - just close modal
closeModal();
return true;
}
return true;
}
});

View file

@ -1,150 +0,0 @@
/**
* Domain form AJAX handling
*/
jQuery(document).ready(function($) {
// console.log("Domain form loaded");
// Handle domain form submission
$('#domain-form').on('submit', function(e) {
e.preventDefault();
// Show loading state
const $submitButton = $(this).find('input[type="submit"]');
const originalText = $submitButton.val();
$submitButton.val('Processing...').prop('disabled', true);
// Get form data
const formData = new FormData(this);
formData.append('action', 'rl_mailwarmer_save_domain');
formData.append('security', domain_form_vars.nonce);
// Process checkbox fields
formData.set('domain_in_use', $('#domain_in_use').is(':checked') ? '1' : '');
formData.set('update_dns', $('#update_dns').is(':checked') ? '1' : '');
// AJAX request
$.ajax({
url: domain_form_vars.ajax_url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response.success) {
console.log("Domain added");
// Display success message
$('#form-messages')
.removeClass('notice-error')
.addClass('notice notice-success is-dismissible')
.html('<p>' + response.data.message + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
// Reset form or redirect if not staying on page
if (!$('#stay_on_page').is(':checked') && response.data.permalink) {
// window.location.href = response.data.permalink;
return;
} else if (!$('#stay_on_page').is(':checked')) {
window.location.href = domain_form_vars.domains_page;
return;
}
// Reset form for new submission if staying on page
if (!$('#post_id').val()) {
$('#domain-form')[0].reset();
}
} else {
// Display error message
$('#form-messages')
.removeClass('notice-success')
.addClass('notice notice-error is-dismissible')
.html('<p>' + response.data.message + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
}
},
error: function(xhr, status, error) {
// Display error message
$('#form-messages')
.removeClass('notice-success')
.addClass('notice notice-error is-dismissible')
.html('<p>An error occurred: ' + error + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
},
complete: function() {
// Restore button state
$submitButton.val(originalText).prop('disabled', false);
}
});
});
// Handle delete domain button click
$('.delete-domain-btn').on('click', function(e) {
e.preventDefault();
if (!confirm('Are you sure you want to delete this domain? This action cannot be undone.')) {
return;
}
const postId = $(this).data('id');
// Show loading state
$(this).text('Deleting...').prop('disabled', true);
// AJAX request to delete domain
$.ajax({
url: domain_form_vars.ajax_url,
type: 'POST',
data: {
action: 'rl_mailwarmer_delete_domain',
security: domain_form_vars.delete_nonce,
post_id: postId
},
success: function(response) {
if (response.success) {
// Redirect to domains list page
window.location.href = domain_form_vars.domains_page;
} else {
alert(response.data.message || 'Error deleting domain');
}
},
error: function() {
alert('An error occurred while deleting the domain');
}
});
});
// Toggle advanced settings
$('.advanced-toggle').on('click', function() {
$(this).parent().find('.advanced-content').slideToggle();
});
// Helper function to add dismiss button to notices
function addNoticeDismissButton() {
$('.notice.is-dismissible').each(function() {
const $notice = $(this);
const $button = $('<button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>');
// Only add the button if it doesn't exist already
if ($notice.find('.notice-dismiss').length === 0) {
$notice.append($button);
// Handle the dismiss button click
$button.on('click', function() {
$notice.slideUp('fast', function() {
$notice.remove();
});
});
}
});
}
// Initialize dismiss buttons
addNoticeDismissButton();
});

View file

@ -1,169 +0,0 @@
/**
* Email account form AJAX handling
*/
jQuery(document).ready(function($) {
// Handle email account form submission
$('#email-account-form').on('submit', function(e) {
e.preventDefault();
// Email validation
if (!validateEmail()) {
return;
}
// Show loading state
const $submitButton = $(this).find('input[type="submit"]');
const originalText = $submitButton.val();
$submitButton.val('Processing...').prop('disabled', true);
// Get form data
const formData = new FormData(this);
formData.append('action', 'rl_mailwarmer_save_email_account');
formData.append('security', email_form_vars.nonce);
// AJAX request
$.ajax({
url: email_form_vars.ajax_url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response.success) {
// Display success message
$('#form-messages')
.removeClass('notice-error')
.addClass('notice notice-success is-dismissible')
.html('<p>' + response.data.message + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
// Reset form or redirect if not staying on page
if (!$('#stay_on_page').is(':checked') && response.data.permalink) {
// window.location.href = response.data.permalink;
return;
} else if (!$('#stay_on_page').is(':checked')) {
window.location.href = email_form_vars.email_accounts_page;
return;
}
// Reset form for new submission if staying on page
if (!$('#post_id').val()) {
$('#email-account-form')[0].reset();
}
} else {
// Display error message
$('#form-messages')
.removeClass('notice-success')
.addClass('notice notice-error is-dismissible')
.html('<p>' + response.data.message + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
}
},
error: function(xhr, status, error) {
// Display error message
$('#form-messages')
.removeClass('notice-success')
.addClass('notice notice-error is-dismissible')
.html('<p>An error occurred: ' + error + '</p>')
.show();
// Add a dismiss button to the notice
addNoticeDismissButton();
},
complete: function() {
// Restore button state
$submitButton.val(originalText).prop('disabled', false);
}
});
});
// Handle delete email account button click
$('.delete-email-account-btn').on('click', function(e) {
e.preventDefault();
if (!confirm('Are you sure you want to delete this email account? This action cannot be undone.')) {
return;
}
const postId = $(this).data('id');
// Show loading state
$(this).text('Deleting...').prop('disabled', true);
// AJAX request to delete email account
$.ajax({
url: email_form_vars.ajax_url,
type: 'POST',
data: {
action: 'rl_mailwarmer_delete_email_account',
security: email_form_vars.delete_nonce,
post_id: postId
},
success: function(response) {
if (response.success) {
// Redirect to email accounts list page
window.location.href = email_form_vars.email_accounts_page;
} else {
alert(response.data.message || 'Error deleting email account');
}
},
error: function() {
alert('An error occurred while deleting the email account');
}
});
});
// Email validation function
function validateEmail() {
const domain_id = $("#domain_id").val();
const email = $("#email_address").val();
if (!domain_id || !email) return true;
const domain_text = $("#domain_id option:selected").text();
const emailDomain = email.split("@")[1];
if (emailDomain !== domain_text) {
alert("Email address must match the selected domain: " + domain_text);
return false;
}
return true;
}
// Add validation to form submission
$("#email_address, #domain_id").on("change", validateEmail);
// Toggle advanced settings
$('.advanced-toggle').on('click', function() {
$(this).parent().find('.advanced-content').slideToggle();
});
// Helper function to add dismiss button to notices
function addNoticeDismissButton() {
$('.notice.is-dismissible').each(function() {
const $notice = $(this);
const $button = $('<button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>');
// Only add the button if it doesn't exist already
if ($notice.find('.notice-dismiss').length === 0) {
$notice.append($button);
// Handle the dismiss button click
$button.on('click', function() {
$notice.slideUp('fast', function() {
$notice.remove();
});
});
}
});
}
// Initialize dismiss buttons
addNoticeDismissButton();
});

View file

@ -1,36 +0,0 @@
jQuery(document).ready(function ($) {
$('#generate-conversation').on('click', function () {
const initiatedBy = $('#initiated_by').val();
const subject = $('#subject').val();
const length = $('#length').val();
$.ajax({
url: rlGenerateConversation.ajax_url,
type: 'POST',
data: {
action: 'rl_generate_conversation',
nonce: rlGenerateConversation.nonce,
post_id: rlGenerateConversation.post_id,
initiated_by: initiatedBy,
subject: subject,
length: length,
},
beforeSend: function () {
$('#generate-conversation').text('Generating...').prop('disabled', true);
},
success: function (response) {
$('#generate-conversation').text('Generate Conversation').prop('disabled', false);
if (response.success) {
console.log('Conversation generated successfully! ID: ' + response.data.conversation_id);
} else {
alert('Error: ' + response.data.message);
}
},
error: function () {
$('#generate-conversation').text('Generate Conversation').prop('disabled', false);
alert('Failed to generate conversation. Please try again.');
},
});
});
});

View file

@ -17,14 +17,13 @@ jQuery(document).ready(function ($) {
success: function (response) {
console.log(response);
if (response.success) {
console.log(response.data);
// 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(response.data);
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>');
}

View file

@ -1,35 +0,0 @@
jQuery(document).ready(function ($) {
// console.log("Loaded check-domain-health.js");
$('#check-domain-health-button').on('click', function (e) {
e.preventDefault();
//var postId = $('#post_ID').val(); // Get the current post ID
var postData = {
action: 'rl_mailwarmer_check_domain_health',
post_id: rlMailWarmer_public.post_id,
security: rlMailWarmer_public.nonce,
};
// console.log("AJAX URL: " + rlMailWarmer_public.ajax_url);
// console.log("Post Action: " + postData.action);
// console.log("Post postId: " + postData.post_id);
// console.log("Post security: " + postData.security);
$('#domain-health-result').html('<p>Checking domain health...</p>');
$.ajax({
url: rlMailWarmer_public.ajax_url,
type: 'POST',
data: postData,
success: function (response) {
if (response.success) {
$('#domain-health-result').html('<p>Report saved successfully. Post ID: ' + response.data + '</p>');
} else {
$('#domain-health-result').html('<p>Error: ' + response.data + '</p>');
}
},
error: function (xhr, status, error) {
$('#domain-health-result').html('<p>AJAX Error: ' + error + '</p>');
},
});
});
});

View file

@ -1,35 +0,0 @@
jQuery(document).ready(function ($) {
$('#rl_generate_accounts_button').on('click', function () {
const postId = $('#post_ID').val();
const quantity = $('#rl_generate_account_qty').val();
$('#rl_generate_accounts_result').html('<p>Generating accounts...</p>');
$.ajax({
url: rlGenerateAccounts.ajax_url,
method: 'POST',
data: {
action: 'rl_generate_random_accounts',
post_id: postId,
qty: quantity,
security: rlGenerateAccounts.nonce,
},
success: function (response) {
if (response.success) {
const results = response.data;
let resultHtml = '<p>Generated Accounts:</p><ul>';
results.forEach(account => {
resultHtml += `<li>${account.email} (Password: ${account.password})</li>`;
});
resultHtml += '</ul>';
$('#rl_generate_accounts_result').html(resultHtml);
} else {
$('#rl_generate_accounts_result').html(`<p>Error: ${response.data}</p>`);
}
},
error: function () {
$('#rl_generate_accounts_result').html('<p>Failed to generate accounts.</p>');
},
});
});
});

View file

@ -1,27 +0,0 @@
jQuery(document).ready(function ($) {
$('#rl_process_conversations_button').on('click', function () {
const postId = $('#post_ID').val();
$('#rl_process_conversations_result').html('<p>Processing conversations...</p>');
$.ajax({
url: rlProcessConversations.ajax_url,
method: 'POST',
data: {
action: 'rl_process_upcoming_conversations',
post_id: postId,
security: rlProcessConversations.nonce,
},
success: function (response) {
if (response.success) {
$('#rl_process_conversations_result').html(`<p>${response.data}</p>`);
} else {
$('#rl_process_conversations_result').html(`<p>Error: ${response.data}</p>`);
}
},
error: function () {
$('#rl_process_conversations_result').html('<p>Failed to process conversations.</p>');
},
});
});
});

View file

@ -1,14 +0,0 @@
<?php
// log_to_file("Running domain template");
// get_header();
// $list = new PostTypeList('domain', [
// 'title' => 'My Domains',
// 'no_items' => 'You haven\'t added any domains yet.',
// 'delete_confirm' => 'Are you sure you want to delete this domain? This action cannot be undone.'
// ]);
// $list->render();
// get_footer();