Example #1
0
 /**
  * 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');
 }
Example #2
0
 /**
  * _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;
 }