/** * Show_Send_Step_3 * Step 3 shows the user a report including: * - which split test they are sending * - how many subscribers it will be sent to * - which lists/segments they are sending to * * The user has to confirm they want to either schedule the campaign * or they can start sending the split test campaign * or (in either case), they can "cancel" the send * eg they chose the wrong split test to send or something * * @uses Jobs_API * @uses Stats_API * @uses Splittest_Send_API * @uses CheckCronEnabled * @uses Splittest_API::Load * @uses Stats_API::CheckUserStats * @uses Lists_API * @uses Segment_API * @uses GetApi */ public function Show_Send_Step_3() { $send_details = IEM::sessionGet('SplitTestSend'); /** * Check the user has been through step 1 successfully. */ if (!$send_details || !isset($send_details['splitid']) || (int) $send_details['splitid'] <= 0) { FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url); return; } /** * Make sure we're posting a proper form. */ if (empty($_POST)) { FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url); return; } $required_fields = array('sendfromname' => 'EnterSendFromName', 'sendfromemail' => 'EnterSendFromEmail', 'replytoemail' => 'EnterReplyToEmail'); $erors = array(); foreach ($required_fields as $fieldname => $lang_description) { if (!isset($_POST[$fieldname])) { $errors[] = GetLang('Addon_splittest_Send_Step3_' . $lang_description); continue; } $posted_value = trim($_POST[$fieldname]); if ($posted_value == '') { $errors[] = GetLang('Addon_splittest_Send_Step3_' . $lang_description); continue; } } if (!empty($errors)) { $errormsg = implode('<br/>', $errors); FlashMessage(sprintf(GetLang('Addon_splittest_Send_Step3_FieldsMissing'), $errormsg), SS_FLASH_MSG_ERROR, $this->admin_url . '&Action=Send&Step=2'); return; } require_once SENDSTUDIO_API_DIRECTORY . '/jobs.php'; $jobapi = new Jobs_API(); require_once SENDSTUDIO_API_DIRECTORY . '/stats.php'; $statsapi = new Stats_API(); $send_api = $this->GetApi('Splittest_Send'); $send_details['SendFromName'] = $_POST['sendfromname']; $send_details['SendFromEmail'] = $_POST['sendfromemail']; $send_details['ReplyToEmail'] = $_POST['replytoemail']; /** * If the user has access to set bounce details, this will be available. * If they don't, we'll use the email from the settings page. */ if (isset($_POST['bounceemail'])) { $send_details['BounceEmail'] = $_POST['bounceemail']; } else { $send_details['BounceEmail'] = SENDSTUDIO_BOUNCE_ADDRESS; } /** * Set the charset. */ $send_details['Charset'] = SENDSTUDIO_CHARSET; $to_firstname = false; if (isset($_POST['to_firstname']) && (int) $_POST['to_firstname'] > 0) { $to_firstname = (int) $_POST['to_firstname']; } $send_details['To_FirstName'] = $to_firstname; $to_lastname = false; if (isset($_POST['to_lastname']) && (int) $_POST['to_lastname'] > 0) { $to_lastname = (int) $_POST['to_lastname']; } $send_details['To_LastName'] = $to_lastname; $send_details['SendStartTime'] = $send_api->GetServerTime(); foreach (array('success', 'total', 'failure') as $area) { $send_details['EmailResults'][$area] = 0; } $send_details['NotifyOwner'] = 1; /** * Split campaigns have to track opens & link clicks * There's no other way they will work. */ $send_details['TrackOpens'] = $send_details['TrackLinks'] = 1; $send_details['Multipart'] = 0; if (isset($_POST['multipart'])) { $send_details['Multipart'] = 1; } $send_details['EmbedImages'] = 0; if (isset($_POST['embedimages'])) { $send_details['EmbedImages'] = 1; } /** * If cron is enabled, we'll get new info we need to take into account. */ if (self::CheckCronEnabled()) { /** * The default (from above) is to "notify", so if it's not set, don't notify. */ if (!isset($_POST['notifyowner'])) { $send_details['NotifyOwner'] = 0; } /** * If we're not sending immediately, then check the date/time. * We don't allow sending in past dates. */ if (!isset($_POST['sendimmediately'])) { $hrs = $_POST['sendtime_hours']; $am_pm = null; if (isset($_POST['sendtime_ampm'])) { $am_pm = strtolower($_POST['sendtime_ampm']); } if ($am_pm == 'pm' && $hrs < 12) { $hrs += 12; } if ($am_pm == 'am' && $hrs == 12) { $hrs = 0; } $gmt_check_time = AdjustTime(array($hrs, $_POST['sendtime_minutes'], 0, $_POST['datetime']['month'], $_POST['datetime']['day'], $_POST['datetime']['year']), true); $now = $send_api->GetServerTime(); /** * There's a leeway of 5 minutes just in case there are any server/time issues. */ $leeway = 5 * 60; if ($gmt_check_time < $now - $leeway) { FlashMessage(GetLang('Addon_splittest_Send_Step2_SendingInPast'), SS_FLASH_MSG_ERROR, $this->admin_url . '&Action=Send&Step=2'); return; } $send_details['SendStartTime'] = $gmt_check_time; } } $this->template_system->Assign('AdminUrl', $this->admin_url, false); $api = $this->GetApi(); $split_campaign_details = $api->Load($send_details['splitid']); $sendingCampaigns = array(); $send_details['newsletters'] = array(); foreach ($split_campaign_details['splittest_campaigns'] as $campaignid => $campaignname) { $sendingCampaigns[$campaignid] = htmlspecialchars($campaignname, ENT_QUOTES, SENDSTUDIO_CHARSET); $send_details['newsletters'][$campaignid] = $campaignid; } /** * Before saving the job details, randomize the newsletter order. * This is so we don't send the newsletters in the same order every time. */ shuffle($send_details['newsletters']); $send_list = array(); switch ($send_details['sendingto']['sendtype']) { case 'list': require_once SENDSTUDIO_API_DIRECTORY . '/lists.php'; $list_api = new Lists_API(); foreach ($send_details['sendingto']['sendids'] as $listid) { $list_api->Load($listid); $send_list[] = htmlspecialchars($list_api->Get('name'), ENT_QUOTES, SENDSTUDIO_CHARSET); } $this->template_system->Assign('SendingToLists', true); break; case 'segment': require_once SENDSTUDIO_API_DIRECTORY . '/segment.php'; $segment_api = new Segment_API(); foreach ($send_details['sendingto']['sendids'] as $segmentid) { $segment_api->Load($segmentid); $send_list[] = htmlspecialchars($segment_api->Get('segmentname'), ENT_QUOTES, SENDSTUDIO_CHARSET); } // The job expects a 'Segments' element, otherwise it will send to entire lists. $send_details['Segments'] = $send_details['sendingto']['sendids']; $this->template_system->Assign('SendingToSegments', true); break; } $subscriber_count = $send_details['sendsize']; $user = IEM::userGetCurrent(); if ($user->HasAccess('Newsletters')) { $this->template_system->Assign('ApplicationUrl', $this->application_url, false); $this->template_system->Assign('NewsletterView', true); } $send_criteria = array(); // can only send to active subscribers. $send_criteria['Status'] = 'a'; $send_criteria['List'] = $send_details['sendingto']['Lists']; $send_details['SendCriteria'] = $send_criteria; $check_stats = $statsapi->CheckUserStats($user, $subscriber_count, $send_details['SendStartTime']); list($ok_to_send, $not_ok_to_send_reason) = $check_stats; if (!$ok_to_send) { require_once SENDSTUDIO_LANGUAGE_DIRECTORY . '/default/send.php'; FlashMessage(GetLang($not_ok_to_send_reason), SS_FLASH_MSG_ERROR, $this->admin_url . '&Action=Send&Step=2'); return; } /** * The 'job' expects a 'Lists' element, so just point it to the sendingto lists. */ $send_details['Lists'] = $send_details['sendingto']['Lists']; $send_details['SendSize'] = $subscriber_count; $jobcreated = $jobapi->Create('splittest', $send_details['SendStartTime'], $user->userid, $send_details, 'splittest', $send_details['splitid'], $send_details['sendingto']['Lists']); $send_details['Job'] = $jobcreated; IEM::sessionSet('JobSendSize', $subscriber_count); // if we're not using scheduled sending, create the queue and start 'er up! if (!self::CheckCronEnabled()) { require_once SENDSTUDIO_API_DIRECTORY . '/subscribers.php'; $subscriberApi = new Subscribers_API(); $sendqueue = $subscriberApi->CreateQueue('splittest'); $jobapi->StartJob($jobcreated); $queuedok = $jobapi->JobQueue($jobcreated, $sendqueue); $queueinfo = array('queueid' => $sendqueue, 'queuetype' => 'splittest', 'ownerid' => $user->userid); if ($send_details['sendingto']['sendtype'] == 'segment') { $subscriberApi->GetSubscribersFromSegment($send_details['sendingto']['sendids'], false, $queueinfo, 'nosort'); } else { $subscriberApi->GetSubscribers($send_criteria, array(), false, $queueinfo, $user->userid); } if (SENDSTUDIO_DATABASE_TYPE == 'pgsql') { $subscriberApi->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues"); } $subscriberApi->RemoveDuplicatesInQueue($sendqueue, 'splittest', $send_details['sendingto']['Lists']); $subscriberApi->RemoveBannedEmails($send_details['sendingto']['Lists'], $sendqueue, 'splittest'); $subscriberApi->RemoveUnsubscribedEmails($send_details['sendingto']['Lists'], $sendqueue, 'splittest'); if (SENDSTUDIO_DATABASE_TYPE == 'pgsql') { $subscriberApi->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues"); } $send_details['SendSize'] = $subscriberApi->QueueSize($sendqueue, 'splittest'); $send_details['Stats'] = array(); $statids = array(); foreach ($send_details['newsletters'] as $newsletterid) { $newsletterstats = $send_details; $newsletterstats['Job'] = $jobcreated; $newsletterstats['Queue'] = $sendqueue; $newsletterstats['SentBy'] = $queueinfo['ownerid']; $newsletterstats['SendType'] = 'splittest'; $newsletterstats['Newsletter'] = $newsletterid; $newsletterstats['Lists'] = $send_details['sendingto']['Lists']; $newsletterstats['SendCriteria'] = $send_criteria; $statid = $statsapi->SaveNewsletterStats($newsletterstats); $statids[] = $statid; $send_details['Stats'][$newsletterid] = $statid; $statsapi->RecordUserStats($user->userid, $jobcreated, $subscriber_count, $send_details['SendStartTime'], $statid); } $send_api->SaveSplitStats($send_details['splitid'], $jobcreated, $statids); $jobapi->PauseJob($jobcreated); } $this->template_system->Assign('sendingCampaigns', $sendingCampaigns); $this->template_system->Assign('sendLists', $send_list); $send_size = $send_details['SendSize']; IEM::sessionSet('SplitTestSendDetails', $send_details); /** * This is used to work out if we should automatically clean up a half-finished send process. * We need to do this because a half-finished send may have taken email-credits from a user * so we need to give them back. */ IEM::sessionSet('SplitTestSend_Cleanup', $send_details); if ($send_size == 1) { $send_size_msg = GetLang('Addon_splittest_Send_Step3_Size_One'); } else { $send_size_msg = sprintf(GetLang('Addon_splittest_Send_Step3_Size_Many'), $this->PrintNumber($send_size)); } $this->template_system->Assign('SendingToNumberOfContacts', $send_size_msg); $this->template_system->Assign('CronEnabled', false); if (self::CheckCronEnabled()) { /** * If cron is enabled, then record the stats allocation now. * This will get fixed up when the actual send starts as it needs to create a stat item for each newsletter. * However, at this stage we just need to record that the user is doing a send so it's removed from their send-allocation. */ $statsapi->RecordUserStats($user->userid, $jobcreated, $subscriber_count, $send_details['SendStartTime']); $user_adjusted_time = AdjustTime($send_details['SendStartTime'], false, GetLang('TimeFormat'), true); $this->template_system->Assign('CronEnabled', true); $this->template_system->Assign('JobScheduleTime', sprintf(GetLang('Addon_splittest_Send_Step3_JobScheduleTime'), $user_adjusted_time)); /** * Mark the job as "waiting". * This will be used on the "manage" page to show it's waiting to send out. */ $send_api->StartJob($jobcreated, $send_details['splitid'], 'w'); } $this->template_system->ParseTemplate('send_step3'); }
/** * _ProcessJob * This method does the "setup work" for a split test campaign. * * If a job is passed in that hasn't been started before, it will set everything up: * - create a "queue" of recipients * - clean the queue (remove banned/duplicate recipients etc) * - set up stats for each newsletter in the split test campaign * - save stats for the user sending the campaign to take off credits etc * * If a job is passed in that has been set up before, it just loads the data up. * * Once it has done either of those, it gives the details to the Splittest_Send_API class * and then calls _ActionJob. * Based on what that returns, it will either mark the job as complete or not. * * If the job is a percentage split test send (send to first X % of a list/segment), then * it sets the appropriate flags etc to make sure we only send to that first X%. * If the job hasn't been set up before, it works out the limit and sets the details in the jobdetails * which are then saved in the database in case the job dies or is paused etc. * * If the job is a percentage split test send and it's time to send to the rest of the list/segment, * then it works out the "best" one to send and sets the appropriate variables for that to happen. * * @param Int $jobid The specific job id we're going to process. * * @uses _jobid * @uses StartJob * @uses PauseJob * @uses LoadJob * @uses GetUser * @uses GetJobQueue * @uses CreateQueue * @uses JobQueue * @uses GetSubscribersFromSegment * @uses GetSubscribers * @uses RemoveDuplicatesInQueue * @uses RemoveBannedEmails * @uses RemoveUnsubscribedEmails * @uses QueueSize * @uses _CalculateBestNewsletter * @uses _FinishJob * @uses _ActionJob * @uses _CalculateBestNewsletter * * @return Boolean Returns whether the job was processed or not. If a job could not be processed, it returns false. Otherwise it returns true. */ private function _ProcessJob($jobid = 0) { if ($jobid <= 0) { return false; } $this->_jobid = $jobid; /** * Load the job, then start it. * We need to do this so when we call "StartJob" we can give it the splitid to "start" as well. */ $jobinfo = $this->LoadJob($jobid); $jobdetails = $jobinfo['jobdetails']; /** * Need to load the split campaign * before starting the job * so if we're in "t"imeout mode, * we can look at the stats * We also need the weighting's from the split campaign * to work it out. */ $this->splitcampaign_details = $this->_splittest_api->Load($jobdetails['splitid']); /** * If it's a "t"imeout, work out the best stat. */ if ($jobinfo['jobstatus'] == 't') { $jobdetails['newsletters'] = $this->_CalculateBestNewsletter($jobdetails['Stats']); /** * Also need to kill off the "percentage_send_maximum" element * as this would cause the job to go into "timeout" mode again. */ unset($jobdetails['percentage_send_maximum']); } if (!$this->StartJob($jobid, $jobdetails['splitid'])) { $this->PauseJob($jobid); return false; } // ----- "Login" to the system as the job's owner. $user = GetUser($jobinfo['ownerid']); IEM::userLogin($jobinfo['ownerid'], false); // ----- $queueid = false; // if there's no queue, start one up. if (!($queueid = $this->GetJobQueue($jobid))) { /** * Randomize the newsletters * It's probably already been done but can't hurt to do it again. */ shuffle($jobdetails['newsletters']); $sendqueue = $this->CreateQueue('splittest'); $queueok = $this->JobQueue($jobid, $sendqueue); $send_criteria = $jobdetails['SendCriteria']; $queueinfo = array('queueid' => $sendqueue, 'queuetype' => 'splittest', 'ownerid' => $jobinfo['ownerid']); if (isset($jobdetails['Segments']) && is_array($jobdetails['Segments'])) { $this->_subscribers_api->GetSubscribersFromSegment($jobdetails['Segments'], false, $queueinfo, 'nosort'); } else { $this->_subscribers_api->GetSubscribers($send_criteria, array(), false, $queueinfo, $sendqueue); } if (SENDSTUDIO_DATABASE_TYPE == 'pgsql') { $this->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues"); } $this->_subscribers_api->RemoveDuplicatesInQueue($sendqueue, 'splittest', $jobdetails['Lists']); $this->_subscribers_api->RemoveBannedEmails($jobdetails['Lists'], $sendqueue, 'splittest'); $this->_subscribers_api->RemoveUnsubscribedEmails($jobdetails['Lists'], $sendqueue, 'splittest'); if (SENDSTUDIO_DATABASE_TYPE == 'pgsql') { $this->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues"); } $jobdetails['SendSize'] = $this->_subscribers_api->QueueSize($sendqueue, 'splittest'); $jobdetails['Stats'] = array(); $jobdetails['SendQueue'] = $sendqueue; /** * Delete the old user stats allocations. * They were all allocated under one stat/job before so the user recorded their send info * so they couldn't set up split test sends and go over their send quota. * * Now, we need to re-allocate them per newsletter being sent. */ $this->_stats_api->DeleteUserStats($jobinfo['ownerid'], $this->_jobid); $statids = array(); foreach ($jobdetails['newsletters'] as $newsletterid) { $newsletterstats = $jobdetails; $newsletterstats['Job'] = $jobid; $newsletterstats['Queue'] = $sendqueue; $newsletterstats['SentBy'] = $queueinfo['ownerid']; $newsletterstats['SendType'] = 'splittest'; $newsletterstats['Newsletter'] = $newsletterid; $newsletterstats['Lists'] = $jobdetails['sendingto']['Lists']; $newsletterstats['SendCriteria'] = $jobdetails['SendCriteria']; $statid = $this->_stats_api->SaveNewsletterStats($newsletterstats); $statids[] = $statid; $jobdetails['Stats'][$newsletterid] = $statid; $this->_stats_api->RecordUserStats($jobinfo['ownerid'], $this->_jobid, $jobdetails['SendSize'], $jobdetails['SendStartTime'], $statid); } $this->SaveSplitStats($jobdetails['splitid'], $this->_jobid, $statids); /** * If it's a percentage send, * work out the number of emails to send for the first percentage * It gets stored in the jobdetails array so it can be saved in the database. */ if ($this->splitcampaign_details['splittype'] == 'percentage') { $max_to_email = ceil($this->splitcampaign_details['splitdetails']['percentage'] / 100 * $jobdetails['SendSize']); $jobdetails['percentage_send_maximum'] = $max_to_email; } /** * Save the job stat details. * Otherwise we could potentially end up with a 'start'ed queue but no stats. */ $this->Set('jobdetails', $jobdetails); $this->UpdateJobDetails(); /** * This is to process the 'queueid' later in the code. */ $queueid = $sendqueue; // This will make sure that the credit warning emails are also being send out from splittest API_USERS::creditEvaluateWarnings($user->GetNewAPI()); } $this->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues"); $queuesize = $this->_subscribers_api->QueueSize($queueid, 'splittest'); $this->_queueid = $queueid; /** * If there is a "percentage_send_maximum" variable in the jobdetails array, * we are sending to the first part of a 'percentage' split test. * * We have to send to the rest of the percentage maximum before we pause the job, * work out the "best" performing campaign and send to that. */ if (isset($jobdetails['percentage_send_maximum'])) { $this->_percentage_send_maximum = (int) $jobdetails['percentage_send_maximum']; } /** * If the _percentage_send_maximum is 0, then "timeout" the job. * We must have hit "pause" right at the end of the initial send process or something. */ if ($this->_percentage_send_maximum !== null && $this->_percentage_send_maximum <= 0) { $this->TimeoutJob($jobid, $this->splitcampaign_details['splitid'], $this->splitcampaign_details['splitdetails']['hoursafter']); IEM::userLogout(); return true; } $this->Set('statids', $jobdetails['Stats']); $this->Set('jobdetails', $jobdetails); $this->Set('jobowner', $jobinfo['ownerid']); /** * There's nothing left? Just mark it as done. */ if ($queuesize == 0) { $this->_FinishJob(); IEM::userLogout(); return true; } $finished = $this->_ActionJob($jobid, $queueid); if ($finished) { $this->_FinishJob(); } IEM::userLogout(); return true; }