965 lines
37 KiB
PHP
965 lines
37 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Helper class for managing Campaigns.
|
|
*/
|
|
class RL_MailWarmer_Campaign_Helper
|
|
{
|
|
|
|
|
|
/**
|
|
* Generate and save a unique 16-digit campaign tracking ID.
|
|
*
|
|
* @param int $campaign_id The ID of the campaign.
|
|
* @return void
|
|
* @throws Exception If unable to generate a unique ID.
|
|
*/
|
|
public static function generate_campaign_tracking_id($campaign_id)
|
|
{
|
|
$max_attempts = 10;
|
|
$attempts = 0;
|
|
|
|
while ($attempts < $max_attempts) {
|
|
$tracking_id = str_pad(mt_rand(0, 9999999999), 10, '0', STR_PAD_LEFT);
|
|
|
|
// Check if the ID is unique (not used by any campaign)
|
|
$query = new WP_Query([
|
|
'post_type' => 'campaign',
|
|
'meta_query' => [
|
|
[
|
|
'key' => 'campaign_tracking_id',
|
|
'value' => $tracking_id,
|
|
'compare' => '=',
|
|
],
|
|
],
|
|
'posts_per_page' => 1,
|
|
'fields' => 'ids'
|
|
]);
|
|
|
|
if (!$query->have_posts()) {
|
|
// The ID is unique, save it
|
|
log_to_file("generate_campaign_tracking_id - Campaign: {$campaign_id} ID: {$tracking_id}");
|
|
update_post_meta($campaign_id, 'campaign_tracking_id', $tracking_id);
|
|
return true;
|
|
}
|
|
$attempts++;
|
|
}
|
|
|
|
// If maximum attempts exceeded, throw an exception
|
|
log_to_file("generate_campaign_tracking_id - Unable to generate a unique campaign tracking ID!");
|
|
throw new Exception(__('Unable to generate a unique campaign tracking ID.', 'rl-mailwarmer'));
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculate the campaign timeline with semi-randomized daily email goals.
|
|
*
|
|
* @param int $campaign_id The campaign post ID.
|
|
* @return array Weekly email schedule with randomized daily goals.
|
|
* @throws Exception If the campaign parameters are invalid.
|
|
*/
|
|
public static function calculate_campaign_timeline($campaign_id)
|
|
{
|
|
|
|
action_log("calculate_campaign_timeline - Creating timeline for campaign {$campaign_id}");
|
|
|
|
// Fetch campaign details
|
|
$warmup_period = (int) get_post_meta($campaign_id, 'warmup_period', true); // Weeks
|
|
$target_volume = (int) get_post_meta($campaign_id, 'target_volume', true); // Daily emails to ramp up to
|
|
$weekend_reduction_factor = get_field('weekend_reduction_factor', $campaign_id) ? (int) get_field('weekend_reduction_factor', $campaign_id) : 25;
|
|
// Make it so the volume is REDUCED by this percentage, not capped at it
|
|
$weekend_reduction_factor = 100 - $weekend_reduction_factor;
|
|
$start_date = get_post_meta($campaign_id, 'start_date', true); // Campaign start date
|
|
|
|
// log_to_file("calculate_campaign_timeline - Target weekend reduction factor: {$weekend_reduction_factor}");
|
|
|
|
if (!$warmup_period || !$target_volume || !$start_date) {
|
|
throw new Exception(__('Invalid campaign parameters.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Fetch ACF options
|
|
$holidays_raw = get_field('calendar_holidays', 'option'); // Holidays in 12/25/25 format
|
|
$holidays = array_map(
|
|
fn($date) => DateTime::createFromFormat('m/d/y', trim($date))->format('Y-m-d'),
|
|
explode("\n", $holidays_raw)
|
|
);
|
|
|
|
$min_starting_volume = (int) get_field('min_starting_email_volume', 'option') ?: 5;
|
|
$max_daily_volume = (int) get_field('max_campaign_daily_email_volume', 'option') ?: 1000;
|
|
|
|
// Calculate starting daily volume (2.5% of target volume)
|
|
$starting_daily_volume = max(ceil($target_volume * 0.025), $min_starting_volume);
|
|
|
|
// Initialize variables
|
|
$timeline = [];
|
|
$total_days = $warmup_period * 7; // Total days in the campaign
|
|
$start_date = new DateTime($start_date);
|
|
|
|
|
|
|
|
// Calculate daily ramp-up rate
|
|
$base_daily_increase = ($target_volume - $starting_daily_volume) / ($total_days * 0.75);
|
|
|
|
log_to_file("calculate_campaign_timeline - Ramping up from $min_starting_volume to $target_volume over $total_days days, increasing by $base_daily_increase (+/- 25%) each day with no more than $max_daily_volume emails in a day");
|
|
|
|
// Generate timeline
|
|
for ($day = 0; $day < $total_days; $day++) {
|
|
$current_date = clone $start_date;
|
|
$current_date->modify("+{$day} days");
|
|
$date_formatted = $current_date->format('Y-m-d');
|
|
|
|
// Adjust for holidays and weekends
|
|
$is_weekend = in_array($current_date->format('N'), [6, 7]);
|
|
$is_holiday = in_array($date_formatted, $holidays);
|
|
$reduction_factor = ($is_weekend || $is_holiday) ? mt_rand($weekend_reduction_factor - 5, $weekend_reduction_factor + 5) / 100 : 1;
|
|
|
|
// log_to_file("calculate_campaign_timeline - Using daily reduction factor: {$reduction_factor}");
|
|
|
|
// Calculate daily volume
|
|
|
|
$percentage_variation = mt_rand(-25, 25);
|
|
$daily_increase = round($base_daily_increase + ($base_daily_increase * $percentage_variation / 100), 2);
|
|
$daily_volume = min(
|
|
ceil(($starting_daily_volume + ($daily_increase * $day)) * $reduction_factor),
|
|
ceil($target_volume + ($target_volume * mt_rand(5, 20)) / 100)
|
|
);
|
|
log_to_file("calculate_campaign_timeline - Daily increase {$daily_increase}");
|
|
|
|
if ($daily_volume > $max_daily_volume) {
|
|
// log_to_file("calculate_campaign_timeline - Max Daily Volume hit for campaign $campaign_id! Capping number of emails");
|
|
$daily_volume = $max_daily_volume;
|
|
}
|
|
|
|
// Calculate percent daily change
|
|
$percent_daily_change = $day > 0 ? (($daily_volume - $last_daily_volume) / $last_daily_volume) * 100 : 0;
|
|
|
|
// Round to 2 decimal places
|
|
$percent_daily_change = round($percent_daily_change, 2);
|
|
|
|
// Add a "+" sign if positive
|
|
$formatted_percent_change = $percent_daily_change > 0 ? '+' . $percent_daily_change : (string)$percent_daily_change;
|
|
|
|
$last_daily_volume = $daily_volume;
|
|
|
|
$timeline[$date_formatted] = [
|
|
'target_volume' => $daily_volume,
|
|
'current_volume' => 0,
|
|
'items_sent' => 0,
|
|
'percent_daily_change' => $formatted_percent_change,
|
|
'daily_increase' => $daily_increase,
|
|
];
|
|
|
|
// log_to_file("calculate_campaign_timeline - $day: $daily_volume");
|
|
}
|
|
|
|
// Save the updated campaign timeline
|
|
update_post_meta($campaign_id, 'campaign_timeline', json_encode($timeline, JSON_PRETTY_PRINT));
|
|
update_post_meta($campaign_id, 'campaign_timeline_json', $timeline);
|
|
|
|
if( self::fill_campaign_timeline($campaign_id, $timeline) )
|
|
{
|
|
// Check the number of saved messages per date
|
|
$message_counts = RL_MailWarmer_DB_Helper::get_message_counts_by_date($campaign_id);
|
|
update_post_meta($campaign_id, 'message_counts', json_encode($message_counts, JSON_PRETTY_PRINT));
|
|
action_log("calculate_campaign_timeline - Finished creating timeline for campaign {$campaign_id}");
|
|
|
|
|
|
return true;
|
|
} else {
|
|
log_to_file("calculate_campaign_timeline - Error filling campaign timeline!");
|
|
throw new Exception(__("Error filling campaign timeline for campaign ID: {$campaign_id}", 'rl-mailwarmer'));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate conversation placeholders of varying lengths and allocate them until all dates in the campaign meet the targets set by RL_MailWarmer_Campaign_Helper::calculate_campaign_timeline()
|
|
*
|
|
* @param int $campaign_id The campaign post ID.
|
|
* @return array Weekly email schedule with randomized daily goals.
|
|
* @throws Exception If the campaign parameters are invalid.
|
|
*/
|
|
public static function fill_campaign_timeline(int $campaign_id, $campaign_timeline = null) {
|
|
$filled_dates = [];
|
|
$weekly_volumes = [];
|
|
$current_week = '';
|
|
$weekly_total = 0;
|
|
$total_conversation_count = 0;
|
|
$total_message_count = 0;
|
|
$campaign_limited = get_post_meta($campaign_id, 'campaign_limited', true);
|
|
if (!$campaign_timeline) {
|
|
try {
|
|
$campaign_timeline = get_field('campaign_timeline', $campaign_id);
|
|
|
|
} catch (Exception $e) {
|
|
|
|
log_to_file("fill_campaign_timeline - No campaign timeline found for campaign ID: {$campaign_id}");
|
|
throw new Exception(__("No campaign timeline found for campaign ID: {$campaign_id}", 'rl-mailwarmer'));
|
|
}
|
|
}
|
|
if (!$campaign_limited ) {
|
|
$ratios = [
|
|
'extra-long' => [
|
|
'percent_of_volume_lower' => 1,
|
|
'percent_of_volume_upper' => 3,
|
|
'num_participants_lower' => 1,
|
|
'num_participants_upper' => 8,
|
|
'num_responses_lower' => 5,
|
|
'num_responses_upper' => 10
|
|
],
|
|
'long' => [
|
|
'percent_of_volume_lower' => 2,
|
|
'percent_of_volume_upper' => 4,
|
|
'num_participants_lower' => 1,
|
|
'num_participants_upper' => 6,
|
|
'num_responses_lower' => 4,
|
|
'num_responses_upper' => 6
|
|
],
|
|
'medium' => [
|
|
'percent_of_volume_lower' => 2,
|
|
'percent_of_volume_upper' => 6,
|
|
'num_participants_lower' => 1,
|
|
'num_participants_upper' => 5,
|
|
'num_responses_lower' => 3,
|
|
'num_responses_upper' => 5
|
|
],
|
|
'short' => [
|
|
'percent_of_volume_lower' => 15,
|
|
'percent_of_volume_upper' => 20,
|
|
'num_participants_lower' => 1,
|
|
'num_participants_upper' => 4,
|
|
'num_responses_lower' => 2,
|
|
'num_responses_upper' => 4
|
|
],
|
|
];
|
|
} else {
|
|
log_to_file("fill_campaign_timeline - Creating a limited campaign");
|
|
$ratios = [
|
|
'short' => [
|
|
'percent_of_volume_lower' => 100,
|
|
'percent_of_volume_upper' => 100,
|
|
'num_participants_lower' => 1,
|
|
'num_participants_upper' => 5,
|
|
'num_responses_lower' => 1,
|
|
'num_responses_upper' => 1
|
|
],
|
|
];
|
|
}
|
|
$warmup_period = (int) get_post_meta($campaign_id, 'warmup_period', true); // Weeks
|
|
$start_date = get_post_meta($campaign_id, 'start_date', true); // Campaign start date
|
|
$total_days = $warmup_period * 7; // Total days in the campaign
|
|
// $total_days = $warmup_period; // Total days in the campaign
|
|
$start_date = date('Y-m-d H:i:s', strtotime($start_date));
|
|
$end_date = date('Y-m-d 23:59:59', strtotime($start_date . " + {$total_days} days"));
|
|
// $end_date = date('Y-m-d H:i', strtotime($start_date . " +{$total_days} days"));
|
|
log_to_file("fill_campaign_timeline - Start: $start_date End: $end_date");
|
|
// log_to_file("fill_campaign_timeline - Campaign timeline $campaign_timeline");
|
|
|
|
// Calculate weekly volumes
|
|
foreach ($campaign_timeline as $date => $data) {
|
|
// log_to_file("fill_campaign_timeline - Campaign timeline date: {$date}", $data);
|
|
$timestamp = strtotime($date);
|
|
$year = date('o', $timestamp); // ISO year
|
|
$week = date('W', $timestamp); // ISO week
|
|
$week_key = sprintf('%d-%02d', $year, $week);
|
|
|
|
if (!isset($weekly_volumes[$week_key])) {
|
|
$weekly_volumes[$week_key] = 0;
|
|
}
|
|
// log_to_file("fill_campaign_timeline - Weekly Volume: ", $weekly_volumes);
|
|
$weekly_volumes[$week_key] += $data['target_volume'];
|
|
}
|
|
|
|
action_log("fill_campaign_timeline - Creating multi-step conversations for campaign {$campaign_id}. Message count: {$total_message_count}");
|
|
|
|
// Process each week
|
|
foreach ($weekly_volumes as $week => $volume) {
|
|
log_to_file("fill_campaign_timeline - Week $week goal: $volume");
|
|
$weekly_scheduled_messages = 0;
|
|
|
|
// Process each conversation length (short, medium, long, etc.)
|
|
foreach ($ratios as $length => $ratio) {
|
|
// Calculate number of conversations for this length
|
|
$percent = mt_rand($ratio['percent_of_volume_lower'], $ratio['percent_of_volume_upper']) / 100;
|
|
$num_conversations = ceil($volume * $percent);
|
|
log_to_file("fill_campaign_timeline - Generating $num_conversations $length conversations");
|
|
|
|
// Skip if no conversations to generate
|
|
if ($num_conversations === 0) continue;
|
|
|
|
// Generate conversations
|
|
for ($i = 0; $i < $num_conversations; $i++) {
|
|
// Check if weekly volume reached
|
|
if ($weekly_scheduled_messages >= $volume) {
|
|
break 2; // Break both loops
|
|
}
|
|
|
|
// Generate random number of responses for this conversation
|
|
$num_particpants = mt_rand($ratio['num_participants_lower'], $ratio['num_participants_upper']);
|
|
|
|
// Generate random number of responses for this conversation
|
|
$num_responses = mt_rand($ratio['num_responses_lower'], $ratio['num_responses_upper']);
|
|
|
|
// Get week start date
|
|
list($year, $weekNum) = explode('-', $week);
|
|
$week_start = date('Y-m-d', strtotime($year . 'W' . $weekNum));
|
|
|
|
if ($week_start < $start_date) {
|
|
$week_start = $start_date;
|
|
}
|
|
|
|
// Generate conversation blueprint
|
|
$conversation_steps = RL_MailWarmer_Conversation_Handler::generate_conversation_blueprint(
|
|
$campaign_id,
|
|
$num_particpants,
|
|
$num_responses,
|
|
$week_start,
|
|
$end_date,
|
|
$filled_dates
|
|
);
|
|
|
|
// Update timeline volumes
|
|
foreach ($conversation_steps as $step) {
|
|
$step_date = date('Y-m-d', strtotime($step['scheduled_for']));
|
|
if (isset($campaign_timeline[$step_date])) {
|
|
$campaign_timeline[$step_date]['current_volume']++;
|
|
$weekly_scheduled_messages++;
|
|
$total_message_count++;
|
|
|
|
// Check if date is now filled
|
|
if ($campaign_timeline[$step_date]['current_volume'] >=
|
|
$campaign_timeline[$step_date]['target_volume']) {
|
|
$filled_dates[] = $step_date;
|
|
}
|
|
}
|
|
}
|
|
|
|
$total_conversation_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
action_log("fill_campaign_timeline - Creating single-step conversations for campaign {$campaign_id}. Message count: {$total_message_count}");
|
|
// log_to_file("fill_campaign_timeline - Campaign Timeline without single-step conversations: ", $campaign_timeline);
|
|
|
|
// Fill remaining capacity with single-message conversations
|
|
log_to_file("fill_campaign_timeline - Filling remaining days with single conversations");
|
|
foreach ($campaign_timeline as $date => $data) {
|
|
$remaining = $data['target_volume'] - $data['current_volume'];
|
|
// log_to_file("fill_campaign_timeline - $date remaining: $remaining");
|
|
|
|
while ($remaining > 0) {
|
|
$conversation_steps = RL_MailWarmer_Conversation_Handler::generate_conversation_blueprint(
|
|
$campaign_id,
|
|
1,
|
|
1,
|
|
$date,
|
|
$date
|
|
);
|
|
|
|
foreach ($conversation_steps as $step) {
|
|
$step_date = date('Y-m-d', strtotime($step['scheduled_for']));
|
|
if (isset($campaign_timeline[$step_date])) {
|
|
$campaign_timeline[$step_date]['current_volume']++;
|
|
$total_message_count++;
|
|
$remaining--;
|
|
|
|
if ($campaign_timeline[$step_date]['current_volume'] >=
|
|
$campaign_timeline[$step_date]['target_volume']) {
|
|
$filled_dates[] = $step_date;
|
|
}
|
|
}
|
|
}
|
|
|
|
$total_conversation_count++;
|
|
}
|
|
}
|
|
|
|
action_log("fill_campaign_timeline - Created {$total_conversation_count} conversations with {$total_message_count} messages for campaign {$campaign_id}");
|
|
// return $campaign_timeline;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse the AI response into structured steps for message creation.
|
|
*
|
|
* @param string $ai_response The JSON string returned by ChatGPT.
|
|
* @return array An array of parsed conversation steps.
|
|
*/
|
|
// public static function parse_ai_response($ai_response) {
|
|
// $steps = json_decode($ai_response, true);
|
|
|
|
// if (json_last_error() !== JSON_ERROR_NONE) {
|
|
// return new WP_Error('invalid_response', __('Failed to parse AI response.', 'rl-mailwarmer'));
|
|
// }
|
|
|
|
// return $steps;
|
|
// }
|
|
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* Add a meta box for generating the campaign timeline.
|
|
*/
|
|
add_action('add_meta_boxes', function () {
|
|
add_meta_box(
|
|
'show_campaign_tracking_id',
|
|
__('Tracking ID', 'rl-mailwarmer'),
|
|
'rl_mailwarmer_render_tracking_id_box',
|
|
'campaign',
|
|
'side',
|
|
'default'
|
|
);
|
|
add_meta_box(
|
|
'generate_campaign_timeline',
|
|
__('Generate Timeline', 'rl-mailwarmer'),
|
|
'rl_mailwarmer_render_generate_timeline_box',
|
|
'campaign',
|
|
'side',
|
|
'default'
|
|
);
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Render the "Tracking ID" meta box.
|
|
*
|
|
* @param WP_Post $post The current post object.
|
|
*/
|
|
function rl_mailwarmer_render_tracking_id_box($post)
|
|
{
|
|
$campaign_tracking_id = get_field('campaign_tracking_id', $post->ID);
|
|
?>
|
|
<span class="tracking_id"><?php echo $campaign_tracking_id; ?></span>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render the "Generate Timeline" meta box.
|
|
*
|
|
* @param WP_Post $post The current post object.
|
|
*/
|
|
function rl_mailwarmer_render_generate_timeline_box($post)
|
|
{
|
|
// Add a nonce for security
|
|
wp_nonce_field('generate_timeline_nonce', 'generate_timeline_nonce_field');
|
|
|
|
// Render the form
|
|
?>
|
|
<form id="generate-timeline-form">
|
|
<p>
|
|
<button type="button" id="generate-timeline-button" class="button button-primary">
|
|
<?php esc_html_e('Generate Timeline', 'rl-mailwarmer'); ?>
|
|
</button>
|
|
</p>
|
|
<div id="generate-timeline-result"></div>
|
|
</form>
|
|
<?php
|
|
}
|
|
|
|
add_action('admin_enqueue_scripts', function ($hook) {
|
|
global $post;
|
|
|
|
// Ensure this is the Add/Edit Campaign screen
|
|
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
|
|
wp_enqueue_script(
|
|
'rl-mailwarmer-generate-timeline-js',
|
|
RL_MAILWARMER_URL . 'js/generate-timeline.js', // Adjust path as needed
|
|
['jquery'],
|
|
null,
|
|
true
|
|
);
|
|
|
|
wp_localize_script('rl-mailwarmer-generate-timeline-js', 'rlMailWarmerGenerateTimeline', [
|
|
'ajax_url' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('generate_timeline_nonce'),
|
|
]);
|
|
}
|
|
});
|
|
|
|
add_action('wp_ajax_rl_mailwarmer_generate_timeline', function () {
|
|
// Verify nonce
|
|
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'generate_timeline_nonce')) {
|
|
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Validate and sanitize input
|
|
$post_id = intval($_POST['post_id']);
|
|
if (!$post_id || get_post_type($post_id) !== 'campaign') {
|
|
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Generate the timeline
|
|
try {
|
|
RL_MailWarmer_Campaign_Helper::calculate_campaign_timeline($post_id);
|
|
// log_to_file("wp_ajax_rl_mailwarmer_generate_timeline - Generated Timeline: ", $timeline);
|
|
// update_post_meta($post_id, "campaign_timeline", $timeline);
|
|
wp_send_json_success("Timeline generated");
|
|
} catch (Exception $e) {
|
|
wp_send_json_error($e->getMessage());
|
|
}
|
|
});
|
|
|
|
|
|
/*
|
|
* Generate Conversation Metabox
|
|
*
|
|
*/
|
|
|
|
add_action('add_meta_boxes', function () {
|
|
add_meta_box(
|
|
'generate_conversation_box', // Unique ID for the meta box
|
|
__('Generate Conversation', 'rl-mailwarmer'), // Title of the meta box
|
|
'rl_mailwarmer_render_conversation_box', // Callback to display the box content
|
|
'campaign', // Post type
|
|
'side', // Context: side, normal, or advanced
|
|
'default' // Priority
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Render the Generate Conversation meta box.
|
|
*
|
|
* @param WP_Post $post The current post object.
|
|
*/
|
|
function rl_mailwarmer_render_conversation_box($post) {
|
|
// Add a nonce for security
|
|
wp_nonce_field('rl_generate_conversation_nonce', 'rl_generate_conversation_nonce_field');
|
|
|
|
// Meta box form
|
|
?>
|
|
<div>
|
|
<label for="initiated_by"><?php esc_html_e('Initiated By (optional)', 'rl-mailwarmer'); ?></label>
|
|
<input type="text" id="initiated_by" name="initiated_by" class="widefat" placeholder="Email address or account ID">
|
|
|
|
<label for="subject"><?php esc_html_e('Subject (optional)', 'rl-mailwarmer'); ?></label>
|
|
<input type="text" id="subject" name="subject" class="widefat" placeholder="Subject line">
|
|
|
|
<label for="length"><?php esc_html_e('Length (optional)', 'rl-mailwarmer'); ?></label>
|
|
<select id="length" name="length" class="widefat">
|
|
<option value=""><?php esc_html_e('Select length', 'rl-mailwarmer'); ?></option>
|
|
<option value="extra-long"><?php esc_html_e('Extra Long', 'rl-mailwarmer'); ?></option>
|
|
<option value="long"><?php esc_html_e('Long', 'rl-mailwarmer'); ?></option>
|
|
<option value="medium"><?php esc_html_e('Medium', 'rl-mailwarmer'); ?></option>
|
|
<option value="short"><?php esc_html_e('Short', 'rl-mailwarmer'); ?></option>
|
|
<option value="single"><?php esc_html_e('Single', 'rl-mailwarmer'); ?></option>
|
|
</select>
|
|
|
|
<button type="button" id="generate-conversation" class="button button-primary" style="margin-top: 10px;">
|
|
<?php esc_html_e('Generate Conversation', 'rl-mailwarmer'); ?>
|
|
</button>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
add_action('admin_enqueue_scripts', function ($hook) {
|
|
global $post;
|
|
|
|
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
|
|
wp_enqueue_script(
|
|
'rl-generate-conversation',
|
|
RL_MAILWARMER_URL . 'js/generate-conversation.js',
|
|
['jquery'],
|
|
null,
|
|
true
|
|
);
|
|
|
|
wp_localize_script('rl-generate-conversation', 'rlGenerateConversation', [
|
|
'ajax_url' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('rl_generate_conversation_nonce'),
|
|
'post_id' => $post->ID,
|
|
]);
|
|
}
|
|
});
|
|
|
|
add_action('wp_ajax_rl_generate_conversation', function () {
|
|
check_ajax_referer('rl_generate_conversation_nonce', 'nonce');
|
|
|
|
$campaign_id = intval($_POST['post_id']);
|
|
$args = [
|
|
'initiated_by' => sanitize_text_field($_POST['initiated_by'] ?? ''),
|
|
'subject' => sanitize_text_field($_POST['subject'] ?? ''),
|
|
'length' => sanitize_text_field($_POST['length'] ?? ''),
|
|
];
|
|
|
|
$result = RL_MailWarmer_Campaign_Helper::generate_conversation($campaign_id, $args);
|
|
|
|
if (is_wp_error($result)) {
|
|
wp_send_json_error(['message' => $result->get_error_message()]);
|
|
}
|
|
|
|
wp_send_json_success(['conversation_id' => $result]);
|
|
});
|
|
|
|
|
|
/**
|
|
* Add the "Generate Blueprint" metabox to the Campaign edit page.
|
|
*/
|
|
add_action('add_meta_boxes', function () {
|
|
add_meta_box(
|
|
'rl_mailwarmer_generate_blueprint',
|
|
__('Generate Blueprint', 'rl-mailwarmer'),
|
|
'rl_mailwarmer_render_generate_blueprint_metabox',
|
|
'campaign', // Post type
|
|
'side',
|
|
'default'
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Render the "Generate Blueprint" metabox.
|
|
*
|
|
* @param WP_Post $post The current post object.
|
|
*/
|
|
function rl_mailwarmer_render_generate_blueprint_metabox($post) {
|
|
wp_nonce_field('rl_mailwarmer_generate_blueprint_nonce', 'rl_mailwarmer_generate_blueprint_nonce_field');
|
|
|
|
?>
|
|
<label for="blueprint_length"><?php esc_html_e('Number of Responses:', 'rl-mailwarmer'); ?></label>
|
|
<input
|
|
type="number"
|
|
id="blueprint_length"
|
|
name="blueprint_length"
|
|
value="1"
|
|
min="1"
|
|
step="1"
|
|
class="widefat"
|
|
/>
|
|
<button id="generate-blueprint" class="button button-primary" style="margin-top: 10px;">
|
|
<?php esc_html_e('Generate Blueprint', 'rl-mailwarmer'); ?>
|
|
</button>
|
|
<div id="blueprint-result" style="margin-top: 10px;"></div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const button = document.getElementById('generate-blueprint');
|
|
const resultDiv = document.getElementById('blueprint-result');
|
|
const inputLength = document.getElementById('blueprint_length');
|
|
|
|
button.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
button.disabled = true;
|
|
resultDiv.textContent = '<?php esc_html_e('Processing...', 'rl-mailwarmer'); ?>';
|
|
|
|
const length = inputLength.value;
|
|
|
|
jQuery.post(ajaxurl, {
|
|
action: 'rl_mailwarmer_generate_blueprint',
|
|
post_id: <?php echo esc_js($post->ID); ?>,
|
|
length: length,
|
|
security: '<?php echo esc_js(wp_create_nonce('rl_mailwarmer_generate_blueprint_nonce')); ?>'
|
|
}, function (response) {
|
|
button.disabled = false;
|
|
|
|
if (response.success) {
|
|
resultDiv.textContent = '<?php esc_html_e('Blueprint generated successfully!', 'rl-mailwarmer'); ?>';
|
|
} else {
|
|
resultDiv.textContent = '<?php esc_html_e('Error:', 'rl-mailwarmer'); ?> ' + response.data.message;
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for generating conversation blueprints.
|
|
*/
|
|
add_action('wp_ajax_rl_mailwarmer_generate_blueprint', function () {
|
|
// Verify nonce
|
|
if (!check_ajax_referer('rl_mailwarmer_generate_blueprint_nonce', 'security', false)) {
|
|
wp_send_json_error(['message' => __('Invalid nonce.', 'rl-mailwarmer')]);
|
|
}
|
|
|
|
// Check permissions
|
|
if (!current_user_can('edit_post', $_POST['post_id'])) {
|
|
wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]);
|
|
}
|
|
|
|
// Validate inputs
|
|
$post_id = intval($_POST['post_id']);
|
|
$length = intval($_POST['length']);
|
|
if ($length < 1) {
|
|
wp_send_json_error(['message' => __('Invalid length specified.', 'rl-mailwarmer')]);
|
|
}
|
|
|
|
try {
|
|
// Generate the blueprint
|
|
RL_MailWarmer_Campaign_Helper::generate_conversation_blueprint($post_id, $length);
|
|
wp_send_json_success();
|
|
} catch (Exception $e) {
|
|
wp_send_json_error(['message' => $e->getMessage()]);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Add a flexbox grid displaying the campaign timeline with gradient colors based on intensity.
|
|
*/
|
|
add_action('edit_form_after_title', 'rl_mailwarmer_display_campaign_timeline');
|
|
|
|
function rl_mailwarmer_display_campaign_timeline($post) {
|
|
// log_to_file("rl_mailwarmer_display_campaign_timeline - Running for {$post->post_title}");
|
|
if ($post->post_type !== 'campaign') {
|
|
return;
|
|
}
|
|
|
|
// Fetch campaign timeline
|
|
$campaign_timeline = get_post_meta($post->ID, 'campaign_timeline_json', true);
|
|
if (empty($campaign_timeline)) {
|
|
echo '<p>' . esc_html__('No timeline available for this campaign.', 'rl-mailwarmer') . '</p>';
|
|
return;
|
|
}
|
|
|
|
$campaign_timeline = $campaign_timeline;
|
|
|
|
// Organize timeline by weeks for grid structure
|
|
$weeks = [];
|
|
$max_volume = 0;
|
|
foreach ($campaign_timeline as $date => $day) {
|
|
$max_volume = max($max_volume, $day['target_volume']);
|
|
$week_number = date('W', strtotime($date));
|
|
if (!isset($weeks[$week_number])) {
|
|
$weeks[$week_number] = [];
|
|
}
|
|
$weeks[$week_number][$date] = $day;
|
|
}
|
|
// log_to_file("edit_form_after_title - Weeks array: ", $weeks);
|
|
|
|
// Add padding days and flatten
|
|
$grid_columns = [];
|
|
foreach ($weeks as $week_number => $week) {
|
|
// Get the first date of this week
|
|
$first_date = array_key_first($week);
|
|
$first_day_num = date('N', strtotime($first_date)); // 1 (Monday) through 7 (Sunday)
|
|
|
|
// Add padding days before if needed
|
|
if ($first_day_num > 1) {
|
|
$padding_days = $first_day_num - 1;
|
|
$monday_date = date('Y-m-d', strtotime("-{$padding_days} days", strtotime($first_date)));
|
|
|
|
for ($i = 0; $i < $padding_days; $i++) {
|
|
$pad_date = date('Y-m-d', strtotime("+{$i} days", strtotime($monday_date)));
|
|
$grid_columns[$pad_date] = [
|
|
'target_volume' => 0,
|
|
'is_padding' => true
|
|
];
|
|
}
|
|
}
|
|
|
|
// Add actual days
|
|
foreach ($week as $date => $day) {
|
|
$day['is_padding'] = false;
|
|
$grid_columns[$date] = $day;
|
|
}
|
|
}
|
|
|
|
// log_to_file("rl_mailwarmer_display_campaign_timeline - Grid Columns: ", $grid_columns);
|
|
|
|
$dates = array_keys($grid_columns);
|
|
$volumes = array_map(function($day) {
|
|
return (isset($day['is_padding']) && $day['is_padding'] == true ) ? 0 : $day;
|
|
}, $grid_columns);
|
|
|
|
// log_to_file("rl_mailwarmer_display_campaign_timeline - Grid volumes: ", $volumes);
|
|
|
|
$chart_data = array_map(function($date, $volume) {
|
|
return [
|
|
'date' => date('M d', strtotime($date)),
|
|
'target' => isset($volume['target_volume']) ? $volume['target_volume'] : null ,
|
|
'sent' => isset($volume['items_sent']) ? $volume['items_sent'] : null
|
|
];
|
|
}, $dates, $volumes);
|
|
|
|
// log_to_file("Chart Data: ", $chart_data);
|
|
|
|
// Gradient colors array
|
|
// $gradients = ["#f12711", "#f24913", "#f36b15", "#f48d17", "#f5af19", "#f8c353", "#fad78c", "#fdebc6", "#ffffff"];
|
|
$gradients = ["#ffffff", "#fdebc6", "#fad78c", "#f8c353", "#f5af19", "#f48d17", "#f36b15", "#f24913", "#f12711"];
|
|
|
|
|
|
// Generate the grid
|
|
?>
|
|
|
|
<div class="timeline-grid">
|
|
<?php
|
|
foreach ($grid_columns as $date => $day):
|
|
if (!empty($day['is_padding'])):
|
|
?>
|
|
<div class="day padding">
|
|
<div class="date"><?php echo esc_html(date('M d, Y', strtotime($date))); ?></div>
|
|
<div class="volume">-</div>
|
|
</div>
|
|
<?php
|
|
else:
|
|
$intensity = ($day['target_volume'] / $max_volume) * 100;
|
|
$gradient_index = min(floor($intensity / (100 / (count($gradients) - 1))), count($gradients) - 1);
|
|
$background_color = $gradients[$gradient_index];
|
|
?>
|
|
<div class="day" style="background-color: <?php echo esc_attr($background_color); ?>;">
|
|
<div class="date"><?php echo esc_html(date('M d, Y', strtotime($date))); ?></div>
|
|
<div class="volume"><?php echo esc_html($day['target_volume']); ?> Emails</div>
|
|
<div class="change"><?php echo esc_html($day['percent_daily_change']); ?>% </div>
|
|
</div>
|
|
<?php
|
|
endif;
|
|
endforeach;
|
|
?>
|
|
</div>
|
|
<!-- <canvas id="timelineChart" style="max-height: 300px;"></canvas> -->
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
const ctx = document.getElementById('timelineChart');
|
|
const data = <?php echo json_encode($chart_data); ?>;
|
|
|
|
// new Chart(ctx, {
|
|
// data: {
|
|
// labels: data.map(row => row.date),
|
|
// datasets: [{
|
|
// type: 'bar',
|
|
// label: 'Target Volume',
|
|
// data: data.map(row => row.target),
|
|
// backgroundColor: 'rgb(255, 0, 0)',
|
|
// borderColor: 'rgb(255, 0, 0)',
|
|
// borderWidth: 1,
|
|
// order: 10,
|
|
// fill: false
|
|
// }, {
|
|
// type: 'bar',
|
|
// label: 'Emails Sent',
|
|
// data: data.map(row => row.sent),
|
|
// backgroundColor: 'rgba(0, 255, 0)',
|
|
// borderColor: 'rgba(0, 255, 0, 0.8)',
|
|
// borderWidth: 1,
|
|
// order: 1
|
|
// }]
|
|
// },
|
|
// options: {
|
|
// responsive: true,
|
|
// scales: {
|
|
// y: {
|
|
// beginAtZero: true,
|
|
// title: {
|
|
// display: true,
|
|
// text: 'Number of Emails'
|
|
// }
|
|
// },
|
|
// x: {
|
|
// stacked: true
|
|
// }
|
|
// }
|
|
// }
|
|
// });
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Delete related conversations & messages when a campaign is deleted
|
|
*/
|
|
add_action('wp_trash_post', 'mailferno_delete_campaign_messages');
|
|
add_action('before_delete_post', 'mailferno_delete_campaign_messages');
|
|
function mailferno_delete_campaign_messages($post_id) {
|
|
// Check if the post is of type 'campaign'
|
|
if (get_post_type($post_id) === 'campaign') {
|
|
log_to_file("mailferno_delete_campaign_messages - Deleting conversations & messages for campaign ID: {$post_id}");
|
|
RL_MailWarmer_DB_Helper::delete_all_conversations_messages($post_id);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Cal-Heatmap
|
|
|
|
// add_action('edit_form_after_title', function ($post) {
|
|
// if ($post->post_type === 'campaign') {
|
|
// echo '<div id="campaign-timeline-heatmap" style="width: 100%; height: 300px; border:1px solid #fff;"></div>';
|
|
// }
|
|
// });
|
|
// add_action('admin_enqueue_scripts', function ($hook) {
|
|
// global $post;
|
|
|
|
// if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
|
|
// // Enqueue D3.js (required for Cal-Heatmap)
|
|
// wp_enqueue_script(
|
|
// 'd3-js',
|
|
// 'https://d3js.org/d3.v7.min.js',
|
|
// [],
|
|
// '7.0.0',
|
|
// true
|
|
// );
|
|
|
|
// // Enqueue Cal-Heatmap
|
|
// wp_enqueue_script(
|
|
// 'cal-heatmap',
|
|
// 'https://unpkg.com/cal-heatmap/dist/cal-heatmap.min.js',
|
|
// ['d3-js'],
|
|
// '4.2.4',
|
|
// true
|
|
// );
|
|
|
|
// // Enqueue Cal-Heatmap CSS
|
|
// wp_enqueue_style(
|
|
// 'cal-heatmap',
|
|
// 'https://unpkg.com/cal-heatmap/dist/cal-heatmap.css',
|
|
// [],
|
|
// '4.2.4'
|
|
// );
|
|
|
|
// // Custom heatmap script
|
|
// wp_enqueue_script(
|
|
// 'rl-mailwarmer-campaign-heatmap',
|
|
// RL_MAILWARMER_URL . 'js/campaign-timeline-heatmap.js',
|
|
// ['cal-heatmap', 'jquery'],
|
|
// null,
|
|
// true
|
|
// );
|
|
|
|
// // Pass data to the script
|
|
// wp_localize_script('rl-mailwarmer-campaign-heatmap', 'rlMailWarmerHeatmap', [
|
|
// 'ajax_url' => admin_url('admin-ajax.php'),
|
|
// 'nonce' => wp_create_nonce('campaign_timeline_nonce'),
|
|
// 'post_id' => $post->ID,
|
|
// ]);
|
|
// }
|
|
// });
|
|
|
|
// add_action('wp_ajax_rl_mailwarmer_get_timeline', function () {
|
|
// // Verify nonce
|
|
// if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'campaign_timeline_nonce')) {
|
|
// wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
|
|
// }
|
|
|
|
// // Validate and sanitize input
|
|
// $post_id = intval($_POST['post_id']);
|
|
// if (!$post_id || get_post_type($post_id) !== 'campaign') {
|
|
// wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
|
|
// }
|
|
|
|
// // Retrieve the timeline
|
|
// $timeline_json = get_post_meta($post_id, 'campaign_timeline', true);
|
|
// $timeline = json_decode($timeline_json, true);
|
|
|
|
// if (!$timeline) {
|
|
// wp_send_json_error(__('No timeline data found.', 'rl-mailwarmer'));
|
|
// }
|
|
|
|
// // Convert timeline to Cal-Heatmap format (timestamp => value)
|
|
// $heatmap_data = [];
|
|
// foreach ($timeline as $date ) {
|
|
// $timestamp = "'" . strtotime($date['date']) . "'"; // Convert to UNIX timestamp
|
|
// $heatmap_data[$timestamp] = $date['target_volume'];
|
|
// }
|
|
|
|
// wp_send_json_success($timeline);
|
|
// });
|