Added files
This commit is contained in:
parent
e0e4433345
commit
cc6dc646b2
14 changed files with 6248 additions and 0 deletions
19
composer.json
Normal file
19
composer.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"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
Normal file
3989
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
432
includes/class-rl-mailwarmer-conversation-helper.php
Normal file
432
includes/class-rl-mailwarmer-conversation-helper.php
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
<?php
|
||||
|
||||
class RL_MailWarmer_Conversation_Handler {
|
||||
|
||||
/**
|
||||
* Process conversations starting within the next 24 hours.
|
||||
*
|
||||
* This function finds conversations with a status of "new" that are scheduled to start within the next 24 hours,
|
||||
* generates them, and updates their status to "generated." Limits to 50 conversations per run.
|
||||
*/
|
||||
public static function process_upcoming_conversations() {
|
||||
log_to_file("process_upcoming_conversations - Running!");
|
||||
global $wpdb;
|
||||
$conversation_table = $wpdb->prefix . 'rl_mailwarmer_conversations';
|
||||
// $current_time = current_time('mysql');
|
||||
$current_time = date('Y-m-d H:i:s', strtotime('-24 hours'));
|
||||
$future_time = date('Y-m-d H:i:s', strtotime('+24 hours'));
|
||||
|
||||
// Fetch up to 50 conversations starting within the next 24 hours
|
||||
$conversations = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM $conversation_table WHERE status = %s AND first_message_timestamp BETWEEN %s AND %s LIMIT 1",
|
||||
'new',
|
||||
$current_time,
|
||||
$future_time
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
// log_to_file("process_upcoming_conversations - Conversations: ", $conversations);
|
||||
|
||||
|
||||
foreach ($conversations as $conversation) {
|
||||
// log_to_file("process_upcoming_conversations - Conversation: ", $conversation);
|
||||
try {
|
||||
// Generate conversation content using AI
|
||||
// $ai_response = self::fetch_conversation_from_ai($conversation);
|
||||
$prompt = $conversation['prompt'];
|
||||
|
||||
// log_to_file("process_upcoming_conversations - AI prompt: ", $prompt);
|
||||
|
||||
// $ai_response = RL_MailWarmer_Campaign_Helper::clean_ai_response(RL_MailWarmer_Campaign_Helper::generate_ai_conversation($prompt));
|
||||
// $ai_response = RL_MailWarmer_Campaign_Helper::generate_ai_conversation($prompt);
|
||||
$ai_response = get_post_meta($conversation['campaign_id'], 'last_ai_response', true);
|
||||
// log_to_file("process_upcoming_conversations - AI Response: ", $ai_response);
|
||||
|
||||
if (is_wp_error($ai_response)) {
|
||||
return $ai_response; // Handle AI generation failure gracefully
|
||||
}
|
||||
|
||||
$updated_conversation_steps = self::merge_timeline_with_ai_response($conversation['conversation_steps'], $ai_response);
|
||||
// log_to_file("process_upcoming_conversations - Merged steps: ", $updated_conversation_steps);
|
||||
// // Update conversation status to "generated"
|
||||
$conversation_table = $wpdb->prefix . 'rl_mailwarmer_conversation';
|
||||
$status = 'new';
|
||||
$result = $wpdb->update(
|
||||
$conversation_table,
|
||||
// [],
|
||||
['status' => $status, 'conversation_steps' => $updated_conversation_steps],
|
||||
['id' => $conversation['id']],
|
||||
['%s'],
|
||||
['%s'],
|
||||
['%d']
|
||||
);
|
||||
// log_to_file("process_upcoming_conversations - Update DB Result: ", $result);
|
||||
|
||||
$update_messages_db = self::update_messages_with_merged_output($conversation['id'], $updated_conversation_steps);
|
||||
log_to_file("process_upcoming_conversations - Result of update_messages_with_merged_output(): ", $update_messages_db);
|
||||
|
||||
// Update the conversation with generated steps
|
||||
// log_to_file("process_upcoming_conversations - Updating conversation_steps");
|
||||
// if (RL_MailWarmer_Campaign_Helper::update_conversation_steps($conversation['id'], $updated_conversation_steps)) {
|
||||
// // log_to_file("process_upcoming_conversations - Updated conversation_steps!");
|
||||
// }
|
||||
|
||||
// Save the generated content to the conversation
|
||||
// RL_MailWarmer_DB_Helper::update_conversation_steps($conversation['id'], $ai_response);
|
||||
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Failed to generate conversation ID {$conversation['id']}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function update_messages_with_merged_output($conversation_id, $merged_output) {
|
||||
log_to_file("update_messages_with_merged_output - merged_output: ", $merged_output);
|
||||
global $wpdb;
|
||||
$message_table = $wpdb->prefix . 'rl_mailwarmer_messages';
|
||||
// $merged_output = json_decode($merged_output);
|
||||
|
||||
// Fetch all messages for the given conversation ID
|
||||
$messages = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM $message_table WHERE conversation_id = %d ORDER BY `scheduled_for_timestamp` ASC",
|
||||
$conversation_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
log_to_file("update_messages_with_merged_output - Found Messages: ", $messages);
|
||||
|
||||
if (empty($messages)) {
|
||||
throw new Exception("No messages found for conversation ID $conversation_id.");
|
||||
}
|
||||
|
||||
// Iterate through messages and merged output
|
||||
foreach ($messages as $index => $message) {
|
||||
if (!isset($merged_output[$index])) {
|
||||
continue; // Skip if there's no corresponding merged output
|
||||
}
|
||||
|
||||
$merged = $merged_output[$index];
|
||||
// log_to_file("update_messages_with_merged_output - merged: $merged");
|
||||
|
||||
// Prepare updated data
|
||||
// $updated_data = [
|
||||
// 'status' => $merged['status'],
|
||||
// 'scheduled_for_timestamp' => $merged['scheduled_for'],
|
||||
// 'from_email' => $merged['from'],
|
||||
// 'to_email' => json_encode($merged['to']),
|
||||
// 'cc_email' => json_encode($merged['cc']),
|
||||
// 'subject' => $merged['subject'],
|
||||
// 'body' => $merged['body'],
|
||||
// ];
|
||||
// log_to_file("update_messages_with_merged_output - Updated Data: ", $updated_data);
|
||||
|
||||
// Update the database
|
||||
// $wpdb->update(
|
||||
// $message_table,
|
||||
// $updated_data,
|
||||
// ['id' => $message['id']], // Where clause
|
||||
// [
|
||||
// '%s', // status
|
||||
// '%s', // scheduled_for_timestamp
|
||||
// '%s', // from_email
|
||||
// '%s', // to_email
|
||||
// '%s', // cc_email
|
||||
// '%s', // subject
|
||||
// '%s', // body
|
||||
// ],
|
||||
// ['%d'] // ID
|
||||
// );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a conversation from ChatGPT based on conversation parameters.
|
||||
*
|
||||
* @param array $conversation The conversation data.
|
||||
* @return string The AI-generated conversation as JSON.
|
||||
* @throws Exception If the API call fails.
|
||||
*/
|
||||
private static function fetch_conversation_from_ai($conversation) {
|
||||
// Prepare the prompt for the AI request
|
||||
$prompt = [
|
||||
'profession' => $conversation['profession'],
|
||||
'from' => $conversation['initiated_by'],
|
||||
'to_pool' => json_decode($conversation['to_pool'], true),
|
||||
'subject' => $conversation['subject'],
|
||||
'num_of_replies' => $conversation['num_responses'],
|
||||
'num_of_participants' => $conversation['num_participants'],
|
||||
'max_days' => 3,
|
||||
'can_reply' => json_decode($conversation['reply_pool'], true),
|
||||
'available_to_cc' => json_decode($conversation['cc_pool'], true),
|
||||
'start_date' => $conversation['first_message_timestamp'],
|
||||
'end_date' => date('Y-m-d H:i:s', strtotime('+3 days', strtotime($conversation['first_message_timestamp'])))
|
||||
];
|
||||
|
||||
// Call OpenAI API
|
||||
$response = RL_MailWarmer_Api_Helper::call_openai_api($prompt);
|
||||
|
||||
if (!$response || !isset($response['choices'][0]['text'])) {
|
||||
throw new Exception('Invalid response from OpenAI API');
|
||||
}
|
||||
|
||||
return $response['choices'][0]['text'];
|
||||
}
|
||||
|
||||
public static function merge_timeline_with_ai_response($conversation_json, $ai_response_json) {
|
||||
// Decode the JSON inputs into arrays
|
||||
$conversation = json_decode($conversation_json, true);
|
||||
$ai_response = json_decode($ai_response_json, true);
|
||||
|
||||
// Ensure both arrays have the same number of elements
|
||||
$merged = [];
|
||||
$count_timeline = count($conversation);
|
||||
$count_ai_response = count($ai_response);
|
||||
|
||||
if ($count_ai_response > $count_timeline) {
|
||||
// Trim the AI responses to match the timeline count
|
||||
$ai_response = array_slice($ai_response, 0, $count_timeline);
|
||||
} elseif ($count_ai_response < $count_timeline) {
|
||||
throw new Exception('The number of AI responses is less than the timeline steps.');
|
||||
}
|
||||
|
||||
// Merge corresponding elements
|
||||
foreach ($conversation as $index => $timeline_step) {
|
||||
$ai_message = $ai_response[$index];
|
||||
$merged[] = array_merge($timeline_step, $ai_message);
|
||||
}
|
||||
|
||||
// Return the merged result as JSON
|
||||
return json_encode($merged, JSON_PRETTY_PRINT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
add_action('admin_menu', function () {
|
||||
add_menu_page(
|
||||
__('Conversations', 'rl-mailwarmer'), // Page title
|
||||
__('Conversations', 'rl-mailwarmer'), // Menu title
|
||||
'manage_options', // Capability
|
||||
'rl-mailwarmer-conversations', // Menu slug
|
||||
'rl_mailwarmer_render_conversations_page', // Callback function
|
||||
'dashicons-email', // Icon
|
||||
8 // Position
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Render the Conversations admin page with checkboxes and a Delete button.
|
||||
*/
|
||||
function rl_mailwarmer_render_conversations_page() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'rl_mailwarmer_conversations'; // Conversation table
|
||||
$conversations = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC");
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e('Conversations', 'rl-mailwarmer'); ?></h1>
|
||||
|
||||
<?php if (empty($conversations)) : ?>
|
||||
<p><?php esc_html_e('No conversations found.', 'rl-mailwarmer'); ?></p>
|
||||
<?php else : ?>
|
||||
<form id="conversation-management-form" method="post">
|
||||
<table class="widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50px;">
|
||||
<input type="checkbox" id="check-all-conversations">
|
||||
</th>
|
||||
<th><?php esc_html_e('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('Steps', 'rl-mailwarmer'); ?></th>
|
||||
<th><?php esc_html_e('Prompt', 'rl-mailwarmer'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($conversations as $conversation) : ?>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" name="selected_conversations[]" value="<?php echo esc_attr($conversation->id); ?>">
|
||||
</td>
|
||||
<td><?php echo esc_html($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->conversation_steps); ?></td>
|
||||
<td>
|
||||
<details>
|
||||
<summary><?php esc_html_e('View Prompt', 'rl-mailwarmer'); ?></summary>
|
||||
<pre><?php echo esc_html($conversation->prompt); ?></pre>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<button type="button" id="delete-selected-conversations" 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 conversations
|
||||
const checkAll = document.getElementById('check-all-conversations');
|
||||
const checkboxes = document.querySelectorAll('input[name="selected_conversations[]"]');
|
||||
|
||||
checkAll.addEventListener('change', function () {
|
||||
checkboxes.forEach(checkbox => checkbox.checked = checkAll.checked);
|
||||
});
|
||||
|
||||
// Handle delete button click
|
||||
document.getElementById('delete-selected-conversations').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 conversation to delete.', 'rl-mailwarmer'); ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm('<?php esc_html_e('Are you sure you want to delete the selected conversations?', 'rl-mailwarmer'); ?>')) {
|
||||
jQuery.post(ajaxurl, {
|
||||
action: 'rl_delete_conversations',
|
||||
conversation_ids: selectedIds,
|
||||
}, function (response) {
|
||||
if (response.success) {
|
||||
alert('<?php esc_html_e('Selected conversations deleted successfully.', 'rl-mailwarmer'); ?>');
|
||||
location.reload(); // Reload the page to update the list
|
||||
} else {
|
||||
alert('<?php esc_html_e('Failed to delete conversations.', 'rl-mailwarmer'); ?>');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
function rl_mailwarmer_get_conversations($page, $per_page = 20) {
|
||||
global $wpdb;
|
||||
|
||||
$offset = ($page - 1) * $per_page;
|
||||
|
||||
$total = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}rl_mailwarmer_conversation");
|
||||
$conversations = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}rl_mailwarmer_conversation ORDER BY created_at DESC LIMIT %d OFFSET %d",
|
||||
$per_page,
|
||||
$offset
|
||||
));
|
||||
|
||||
return ['conversations' => $conversations, 'total' => $total];
|
||||
}
|
||||
|
||||
add_action('wp_ajax_rl_delete_conversations', function () {
|
||||
global $wpdb;
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]);
|
||||
}
|
||||
|
||||
$conversation_ids = isset($_POST['conversation_ids']) ? array_map('intval', $_POST['conversation_ids']) : [];
|
||||
|
||||
if (empty($conversation_ids)) {
|
||||
wp_send_json_error(['message' => __('No conversations selected.', 'rl-mailwarmer')]);
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'rl_mailwarmer_conversation';
|
||||
|
||||
// Delete selected conversations
|
||||
$placeholders = implode(',', array_fill(0, count($conversation_ids), '%d'));
|
||||
$query = "DELETE FROM $table_name WHERE id IN ($placeholders)";
|
||||
$result = $wpdb->query($wpdb->prepare($query, $conversation_ids));
|
||||
|
||||
if ($result === false) {
|
||||
wp_send_json_error(['message' => __('Failed to delete conversations.', 'rl-mailwarmer')]);
|
||||
}
|
||||
|
||||
wp_send_json_success(['message' => __('Conversations deleted successfully.', 'rl-mailwarmer')]);
|
||||
});
|
||||
|
||||
|
||||
// Add the metabox
|
||||
add_action('add_meta_boxes', 'rl_mailwarmer_add_process_conversations_metabox');
|
||||
function rl_mailwarmer_add_process_conversations_metabox() {
|
||||
add_meta_box(
|
||||
'rl-process-conversations',
|
||||
__('Process Upcoming Conversations', 'rl-mailwarmer'),
|
||||
'rl_mailwarmer_process_conversations_metabox_callback',
|
||||
'campaign',
|
||||
'side',
|
||||
'default'
|
||||
);
|
||||
}
|
||||
|
||||
// Metabox callback function
|
||||
function rl_mailwarmer_process_conversations_metabox_callback($post) {
|
||||
// Nonce field for security
|
||||
wp_nonce_field('rl_process_conversations_nonce', 'rl_process_conversations_nonce_field');
|
||||
|
||||
?>
|
||||
<button type="button" id="rl_process_conversations_button" class="button button-primary">
|
||||
<?php _e('Process Upcoming Conversations', 'rl-mailwarmer'); ?>
|
||||
</button>
|
||||
<div id="rl_process_conversations_result" style="margin-top: 10px;"></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Enqueue JavaScript
|
||||
add_action('admin_enqueue_scripts', 'rl_mailwarmer_enqueue_process_conversations_script');
|
||||
function rl_mailwarmer_enqueue_process_conversations_script($hook) {
|
||||
if ($hook === 'post.php' || $hook === 'post-new.php') {
|
||||
wp_enqueue_script(
|
||||
'rl-process-conversations',
|
||||
RL_MAILWARMER_URL . 'js/rl-process-conversations.js',
|
||||
['jquery'],
|
||||
'1.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('rl-process-conversations', 'rlProcessConversations', [
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('rl_process_conversations_nonce'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// AJAX handler
|
||||
add_action('wp_ajax_rl_process_upcoming_conversations', 'rl_mailwarmer_process_upcoming_conversations_handler');
|
||||
function rl_mailwarmer_process_upcoming_conversations_handler() {
|
||||
check_ajax_referer('rl_process_conversations_nonce', 'security');
|
||||
|
||||
$post_id = intval($_POST['post_id']);
|
||||
|
||||
if (!$post_id) {
|
||||
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
|
||||
}
|
||||
|
||||
try {
|
||||
// Call the function to process upcoming conversations
|
||||
RL_MailWarmer_Conversation_Handler::process_upcoming_conversations($post_id);
|
||||
|
||||
wp_send_json_success(__('Conversations processed successfully.', 'rl-mailwarmer'));
|
||||
} catch (Exception $e) {
|
||||
wp_send_json_error(__('Error processing conversations: ', 'rl-mailwarmer') . $e->getMessage());
|
||||
}
|
||||
}
|
||||
627
includes/class-rl-mailwarmer-email-account-helper.php
Normal file
627
includes/class-rl-mailwarmer-email-account-helper.php
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
<?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') {
|
||||
return new WP_Error('invalid_post', __('Invalid email account post.', 'rl-mailwarmer'));
|
||||
}
|
||||
|
||||
// Fetch email provider and override defaults with saved values
|
||||
$email_provider_id = get_post_meta($post->ID, 'email_provider', true);
|
||||
// log_to_file("check_mail_login - ");
|
||||
|
||||
// log_to_file("check_mail_login - Email Provider ID $email_provider_id");
|
||||
$defaults = $email_provider_id ? self::get_provider_defaults($email_provider_id) : [];
|
||||
// log_to_file("check_mail_login - Email Provider Defaults: ", $defaults);
|
||||
|
||||
// Fetch saved settings
|
||||
$saved_settings = [
|
||||
'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($post->post_title, $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($post->post_title, $settings);
|
||||
// log_to_file("check_mail_login - SMTP Result for " . $post->post_title . ": ", $imap_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 string $email The email address.
|
||||
* @param array $settings The server settings (imap_server, imap_port, imap_password).
|
||||
* @return bool True if the connection is successful, false otherwise.
|
||||
*/
|
||||
private static function validate_imap_connection($email, $settings)
|
||||
{
|
||||
if ( empty($settings['imap_server']) || empty($settings['imap_port']) ) {
|
||||
return false; // Missing required settings
|
||||
}
|
||||
|
||||
if (!empty($settings['imap_password'])) {
|
||||
$password = $settings['imap_password'];
|
||||
} else {
|
||||
$password = $settings['mail_password'];
|
||||
}
|
||||
|
||||
// Try connecting to the IMAP server
|
||||
$imap_stream = @imap_open(
|
||||
'{' . $settings['imap_server'] . ':' . $settings['imap_port'] . '/imap/ssl}',
|
||||
$email,
|
||||
$password
|
||||
);
|
||||
|
||||
if ($imap_stream) {
|
||||
imap_close($imap_stream); // Close connection if successful
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Connection failed
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate an SMTP connection for an email account using Symfony Mailer.
|
||||
*
|
||||
* @param string $email The email address.
|
||||
* @param array $settings The server settings (smtp_server, smtp_port, smtp_password).
|
||||
* @return bool True if the connection is successful, false otherwise.
|
||||
*/
|
||||
private static function validate_smtp_connection($email, $settings)
|
||||
{
|
||||
if (empty($settings['smtp_server']) || empty($settings['smtp_port']) ) {
|
||||
return false; // Missing required settings
|
||||
}
|
||||
|
||||
if (!empty($settings['smtp_password'])) {
|
||||
$password = $settings['smtp_password'];
|
||||
} else {
|
||||
$password = $settings['mail_password'];
|
||||
}
|
||||
|
||||
$signature = str_replace('\n', PHP_EOL, $settings['email_signature']);
|
||||
$test_to_email = "ruben@redlotusaustin.com";
|
||||
$email_body = "This is a test email to verify SMTP connection for {$email}\n\n{$signature}";
|
||||
|
||||
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);
|
||||
|
||||
$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')]);
|
||||
});
|
||||
|
||||
513
includes/class-rl-mailwarmer-message-handler.php
Normal file
513
includes/class-rl-mailwarmer-message-handler.php
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Helper functions for the conversation messages
|
||||
*/
|
||||
|
||||
use Symfony\Component\Mime\Email;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Mailer\Transport\Transport;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use PhpImap\Mailbox;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// 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 1",
|
||||
'pending',
|
||||
current_time('mysql')
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
if (empty($messages)) {
|
||||
// log_to_file("process_pending_messages - messages empty");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Update message status to 'completed' on success
|
||||
if ($result) {
|
||||
self::update_message_status($message['id'], 'completed');
|
||||
} else {
|
||||
self::update_message_status($message['id'], 'failed');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Handle errors gracefully and log them
|
||||
log_to_file('Error processing message ID ' . $message['id'] . ': ' . $e->getMessage());
|
||||
self::update_message_status($message['id'], 'failed');
|
||||
}
|
||||
sleep(3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
$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 = is_array($message['to_email']) ? $message['to_email'] : explode(',', $message['to_email']);
|
||||
$cc_emails = is_array($message['cc']) ? $message['cc'] : explode(',', $message['cc']);
|
||||
|
||||
return [
|
||||
'connection_info' => $connection_info,
|
||||
'to' => array_filter(array_map('trim', $to_emails)),
|
||||
'cc' => array_filter(array_map('trim', $cc_emails)),
|
||||
'subject' => $message['subject'],
|
||||
'body' => $message['body'],
|
||||
'from' => $message['from_email'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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']
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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'];
|
||||
|
||||
// Check required fields
|
||||
if (empty($email_data['to']) || empty($email_data['from']) || empty($email_data['subject']) || empty($email_data['body'])) {
|
||||
throw new Exception(__('Missing required fields for sending the email.', 'rl-mailwarmer'));
|
||||
}
|
||||
|
||||
// Create the SMTP transport
|
||||
// log_to_file("send_message - Creating 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
|
||||
$mailer = new Symfony\Component\Mailer\Mailer($transport);
|
||||
|
||||
// Build the email
|
||||
// log_to_file("send_message - Building Mail");
|
||||
$email = (new Email())
|
||||
->from($email_data['from'])
|
||||
->to(...$email_data['to'])
|
||||
->subject($email_data['subject'])
|
||||
->html($email_data['body']);
|
||||
|
||||
// Add CCs if present
|
||||
if (!empty($email_data['cc'])) {
|
||||
$email->cc(...$email_data['cc']);
|
||||
}
|
||||
|
||||
// Attempt to send the email
|
||||
// log_to_file("send_message - Trying to send");
|
||||
try {
|
||||
$mailer->send($email);
|
||||
log_to_file("send_message - Successfully sent SMTP mail from ", $email_data['from']);
|
||||
return true; // Email sent successfully
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
error_log('Error sending email: ' . $e->getMessage());
|
||||
return false; // Sending failed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// Extract connection info
|
||||
$connection_info = $email_data['connection_info'];
|
||||
|
||||
// Validate required fields
|
||||
if (empty($email_data['to']) || empty($email_data['from']) || empty($email_data['subject']) || empty($email_data['body'])) {
|
||||
throw new Exception(__('Missing required fields for replying to the email.', 'rl-mailwarmer'));
|
||||
}
|
||||
|
||||
// Attempt to find the original email via IMAP
|
||||
|
||||
|
||||
|
||||
// // Attempt to find the original email and reply via IMAP
|
||||
// try {
|
||||
// $imap = new \PhpImap\Mailbox(
|
||||
// sprintf('{%s:%d/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']),
|
||||
// $connection_info['imap_username'],
|
||||
// $connection_info['imap_password'],
|
||||
// null,
|
||||
// 'UTF-8'
|
||||
// );
|
||||
|
||||
// // Search for the email with the matching subject
|
||||
// $emails = $imap->searchMailbox('SUBJECT "' . addslashes($email_data['subject']) . '"');
|
||||
// if (!empty($emails)) {
|
||||
// // Fetch the email data
|
||||
// $original_email = $imap->getMail($emails[0]);
|
||||
|
||||
// // Prepare and send the reply via IMAP
|
||||
// $imap->reply(
|
||||
// $emails[0],
|
||||
// $email_data['body'],
|
||||
// ['from' => $email_data['from'], 'cc' => $email_data['cc']]
|
||||
// );
|
||||
|
||||
// return true; // Reply sent successfully via IMAP
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// log_to_file("reply_message - Trying to reply via IMAP");
|
||||
$imap = new \PhpImap\Mailbox(
|
||||
sprintf('{%s:%d/imap/ssl}', $connection_info['imap_server'], $connection_info['imap_port']),
|
||||
$connection_info['username'],
|
||||
$connection_info['mail_password'],
|
||||
null,
|
||||
'UTF-8'
|
||||
);
|
||||
|
||||
// Search for the email with the matching subject
|
||||
// log_to_file("reply_message - Searching for message");
|
||||
$emails = $imap->searchMailbox('SUBJECT "' . addslashes($email_data['subject']) . '"');
|
||||
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']);
|
||||
|
||||
$mailer = new Mailer($transport);
|
||||
|
||||
$reply_email = (new Email())
|
||||
->from($email_data['from'])
|
||||
->to(...$email_data['to'])
|
||||
->subject('Re: ' . $original_email->subject)
|
||||
->html($email_data['body']);
|
||||
|
||||
// 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
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
log_to_file('IMAP Error: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// Fallback to SMTP if IMAP fails
|
||||
try {
|
||||
// log_to_file("reply_message - Falling back to SMTP");
|
||||
$smtp_reply = (new Email())
|
||||
->from($email_data['from'])
|
||||
->to(...$email_data['to'])
|
||||
->subject($email_data['subject'])
|
||||
->html($email_data['body']);
|
||||
|
||||
// Add CCs if present
|
||||
if (!empty($email_data['cc'])) {
|
||||
$smtp_reply->cc(...$email_data['cc']);
|
||||
}
|
||||
|
||||
// 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($connection_info['username']);
|
||||
$transport->setPassword($connection_info['mail_password']);
|
||||
|
||||
// Create the mailer
|
||||
$mailer = new Symfony\Component\Mailer\Mailer($transport);
|
||||
$mailer->send($smtp_reply);
|
||||
log_to_file("reply_message - Sent reply via fallback SMTP from ", $email_data['from']);
|
||||
// log_to_file('reply_message - SMTP Send Success (?)');
|
||||
|
||||
return true; // Fallback SMTP reply sent successfully
|
||||
} catch (Exception $e) {
|
||||
log_to_file('reply_message - SMTP Error: ' . $e->getMessage());
|
||||
return false; // Reply failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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",
|
||||
'pending',
|
||||
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_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.processed_count;
|
||||
} 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_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()");
|
||||
$processed_count = RL_MailWarmer_Message_Handler::process_pending_messages();
|
||||
|
||||
wp_send_json_success(['processed_count' => $processed_count]);
|
||||
} catch (Exception $e) {
|
||||
wp_send_json_error(['message' => $e->getMessage()]);
|
||||
}
|
||||
});
|
||||
155
includes/class-rl-mailwarmer-message-helper.php
Normal file
155
includes/class-rl-mailwarmer-message-helper.php
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?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')]);
|
||||
});
|
||||
143
includes/class-rl-mailwarmer-post-tables.php
Normal file
143
includes/class-rl-mailwarmer-post-tables.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?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']) : 10;
|
||||
$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 esc_html($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' => __('«'),
|
||||
'next_text' => __('»'),
|
||||
'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)
|
||||
);
|
||||
}
|
||||
}
|
||||
185
includes/rl-mailwarmer-importer.php
Normal file
185
includes/rl-mailwarmer-importer.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?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>';
|
||||
}
|
||||
38
js/admin-update-mx.js
Normal file
38
js/admin-update-mx.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
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>');
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
36
js/generate-conversation.js
Normal file
36
js/generate-conversation.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
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.');
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
35
js/public-check-domain-health.js
Normal file
35
js/public-check-domain-health.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
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>');
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
35
js/rl-generate-accounts.js
Normal file
35
js/rl-generate-accounts.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
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>');
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
27
js/rl-process-conversations.js
Normal file
27
js/rl-process-conversations.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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>');
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
14
templates/domain-list.php
Normal file
14
templates/domain-list.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?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();
|
||||
Loading…
Add table
Reference in a new issue