<?php
/**
 * This file handles the processing of sending a split test.
 * It is a separate file so that the size and complexity of the main split test addon file
 * is kept to a minimum.
 * That is required so when an addon is included,
 * memory usage, code complexity (ie php parsing time) is kept as low as possible.
 *
 * @package Interspire_Addons
 * @subpackage Addons_splittest
 */

/**
 * If this class has been called from outside the Addons_splittest class, then die.
 * It's going to be an invalid request.
 */
if (!class_exists('Addons_splittest', false)) {
	die;
}

/**
 * Addons_splittest_Send
 * This class handles all aspects of sending a split test, from
 * - displaying the forms
 * - checking form values (ie you're posting right data)
 * - printing success/error messages at each step
 * etc
 *
 * It can only be included by the Addons_splittest class.
 * Trying to include it by itself will cause errors and the script will just die.
 *
 * @usedby Addons_splittest
 */
class Addons_splittest_Send extends Addons_splittest
{

	/**
	 * addon_id
	 * We need to masquerade as the 'splittest' addon
	 * So we get the right template path, template url,
	 * admin url and so on from the parent Interspire_Addons system.
	 *
	 * @var String
	 *
	 * @usedby Interspire_Addons::__construct
	 */
	protected $addon_id = 'splittest';

	/**
	 * __construct
	 *
	 * Calls the parent constructor to set up various options including:
	 * - template path
	 * - template url
	 * - admin url
	 * - include the language file(s)
	 * etc
	 *
	 * @uses Interspire_Addons::__construct
	 *
	 * @return Void Doesn't return anything.
	 */
	function __construct()
	{
		parent::__construct();
	}

	/**
	 * Show_Send_Step_1
	 * This shows the first step of the send process where the user chooses a list or segment to send to.
	 *
	 * You cannot send to part of a list (eg by searching),
	 * you can only send a split test to whole list(s) or whole segment(s).
	 *
	 * If the user has no lists or segments, they are taken back to the default page which shows a list of split test campaigns
	 * They are shown a message saying they need to create/be given access to a list or segment before trying to do a send.
	 *
	 * If there are lists or segments, then the user is shown a list of them to choose from.
	 * They must select at least one list or segment before going to the next step.
	 *
	 * It also clears out the session variable which holds the split test send details.
	 *
	 * Step 1 also checks what type of split campaign you are trying to send.
 	 * You can't send 'percentage' split campaigns if cron is not enabled.
	 * The 'percentage' split campaigns send a percentage of emails to a random selection of subscribers
	 * then pauses, then sends the rest of the emails to the 'winner' (which has the most opens/clicked links).
	 * This could probably be worked out if you don't have cron support but for now,
	 * you can't send 'percentage' split campaigns if there is no cron.
	 *
	 * @uses GetUser
	 * @uses User_API::GetLists
	 * @uses User_API::GetSegmentList
	 * @uses Session::Remove
	 * @uses FlashMessage
	 */
	public function Show_Send_Step_1()
	{
		$user = IEM::getCurrentUser();
		$user_lists = $user->GetLists();

		$user_segments = $user->GetSegmentList();
		if (empty($user_lists) && empty($user_segments)) {
			FlashMessage(GetLang('Addon_splittest_Send_NoListsOrSegments'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		$splitid = 0;
		if (isset($_GET['id'])) {
			$splitid = (int)$_GET['id'];
		}

		if ($splitid <= 0) {
			FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		$api = $this->GetApi('Splittest');
		$split_campaign_details = $api->Load($splitid);
		if (empty($split_campaign_details)) {
			FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		if (!empty($split_campaign_details['jobstatus']) && $split_campaign_details['jobstatus'] != 'c') {
			FlashMessage(GetLang('Addon_splittest_Send_CannotSend_StillSending'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		/**
		 * If it's a percentage send, then check if cron is enabled.
		 * If it's not, then don't allow the user to go any further.
		 */
		if ($split_campaign_details['splittype'] == 'percentage') {
			if (!self::CheckCronEnabled()) {
				$message = sprintf(GetLang('Addon_splittest_CannotSendPercentage_NoCron'), IEM::urlFor('Settings', ['Tab' => 4]));
				FlashMessage($message, SS_FLASH_MSG_ERROR, $this->admin_url);
				return;
			}
		}

		/**
		 * Only show segment options if the user has access to send to segments.
		 */
		$this->template_system->Assign('DisplaySegments', $user->HasAccess('Segments', 'Send'));

		IEM::sessionRemove('SplitTestSend');
		$send_details = [];
		$send_details['splitid'] = $splitid;
		IEM::sessionSet('SplitTestSend', $send_details);

		$this->template_system->Assign('FlashMessages', GetFlashMessages(), false);

		$this->template_system->Assign('AdminUrl', $this->admin_url, false);

		$this->template_system->Assign('user_lists', $user_lists);
		$this->template_system->Assign('user_segments', $user_segments);

		$this->template_system->ParseTemplate('send_step1');
	}

	/**
	 * Show_Send_Step_2
	 * This handles the second step of the send process where
	 *
	 * 1) the first step is verified in php
	 * - checks you are choosing a list or segment
	 * - checks the list/segment id's are valid (ie they are proper id's and not non-numeric values)
	 * - checks the list or segment has contacts in it
	 *
	 * If that checks out ok, the second step shows the form where you set:
	 * - from details
	 * - bounce details
	 * - whether to send the campaigns as multipart (where possible)
	 * - whether to embed images or not (where possible)
	 * - choose the first/last name custom fields
	 * - when to send the split test (if cron is enabled)
	 *
	 * @uses FlashMessage
	 * @uses GetFlashMessages
	 * @uses List_API::Load
	 * @uses List_API::GetCustomFields
	 * @uses Segment_API::Load
	 * @uses GetUser
	 * @uses User_API::HasAccess
	 * @uses CheckCronEnabled
	 */
	public function Show_Send_Step_2()
	{
		$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;
		}

		$step1_url = $this->admin_url . '&Action=Send&id=' . $send_details['splitid'];

		$flash_messages = GetFlashMessages();

		/**
		 * If we are not posting a form, maybe we're being redirected back to this step from step 3.
		 * If we are, we'll have flash messages.
		 *
		 * If there are no post variables and we are not showing any messages, then go back to step 1.
		 */
		if (empty($_POST) && empty($flash_messages)) {
			FlashMessage(GetLang('Addon_splittest_Send_ChooseListOrSegment'), SS_FLASH_MSG_ERROR, $step1_url);
			return;
		}

		/**
		 * If we are posting a form, check we are posting a proper form (we should have lists or segments chosen)
		 */
		if (!empty($_POST)) {
			/**
			 * Work out which option the user chose
			 */
			$sending_to = [];
			$send_type = '';
			switch ((int)$_POST['ShowFilteringOptions']) {
				case 1:
					$send_type = 'list';
					if (isset($_POST['lists'])) {
						$sending_to = $_POST['lists'];
					}
				break;

				case 2:
					$send_type = 'segment';
					if (isset($_POST['segments'])) {
						$sending_to = $_POST['segments'];
					}
				break;

				default:
					FlashMessage(GetLang('Addon_splittest_Send_ChooseListOrSegment'), SS_FLASH_MSG_ERROR, $step1_url);
					return;
				break;
			}

			/**
			 * Make sure the user actually chose some options.
			 */
			if (empty($sending_to)) {
				FlashMessage(GetLang('Addon_splittest_Send_ChooseListOrSegment'), SS_FLASH_MSG_ERROR, $step1_url);
				return;
			}

			/**
			 * Make sure the id's the user chose are valid
			 * If any area invalid (not int's), the user is thrown back to the first step.
			 */
			foreach ($sending_to as $id) {
				$id = (int)$id;
				if ($id <= 0) {
					FlashMessage(GetLang('Addon_splittest_Send_ChooseListOrSegment'), SS_FLASH_MSG_ERROR, $step1_url);
					return;
				}
			}

			/**
			 * After everything has been validated, store the settings and display the next form.
			 */
			$send_details['sendingto'] = [
				'sendtype' => $send_type,
				'sendids' => $sending_to
			];

			IEM::sessionSet('SplitTestSend', $send_details);
		}

		$user = IEM::getCurrentUser();

		/**
		 * Re-set these variables.
		 * They may be coming from the session if we are being redirected back to step 2 from another step.
		 */
	    $send_type = $send_details['sendingto']['sendtype'];
		$sending_to = $send_details['sendingto']['sendids'];

		/**
		 * Get the first list or segment we're sending to.
		 * We need this to set the defaults for the from name, email, reply-to email and bounce details.
		 * It doesn't really matter what the id is, the user can override the options anyway through the form.
		 */
		$id = $sending_to[0];

		$send_size = 0;

		/**
		 * We always need the list api
		 * so we can load the right details regardless of whether the user is sending to a segment or list.
		 */
		require_once SENDSTUDIO_API_DIRECTORY . '/lists.php';
		$list_api = new Lists_API;

		switch ($send_type) {
			case 'list':
				$list_id = $id;
				$listids = $sending_to;

				$user_lists = $user->GetLists();

				foreach ($user_lists as $user_list_id => $user_list_details) {
					if (!in_array($user_list_id, $sending_to)) {
						continue;
					}
					$send_size += $user_list_details['subscribecount'];
				}

				// this is used at a later step to check duplicates etc.
				$send_details['sendingto']['Lists'] = $sending_to;
			break;

			case 'segment':
				require_once SENDSTUDIO_API_DIRECTORY . '/subscribers.php';
				$api = new Subscribers_API;
				$segment_info = $api->GetSubscribersFromSegment($sending_to, true);

				$send_size = $segment_info['count'];

				/**
				 * Since a segment can go across lists,
				 * we only need one list - so just get the first one we come across.
				 * The user can change the details on the fly anyway.
				 */
				$listids = $segment_info['lists'];
				$list_id = $listids[0];

				// this is used at a later step to check duplicates etc.
				$send_details['sendingto']['Lists'] = $listids;
			break;
		}

		if ($send_size <= 0) {
			$var = 'Addon_splittest_Send_NoContacts_';
			if (sizeof($sending_to) == 1) {
				$var .= 'One_';
			} else {
				$var .= 'Many_';
			}
			$var .= $send_type;
			FlashMessage(GetLang($var), SS_FLASH_MSG_ERROR, $step1_url);
			return;
		}

		$send_details['sendsize'] = $send_size;
		IEM::sessionSet('SplitTestSend', $send_details);

		/**
		 * Get the flashmessage to draw the box.
		 * then add to the existing messages (eg we're back here from step 3).
		 */
		if ($send_size == 1) {
			FlashMessage(GetLang('Addon_splittest_Send_Step2_Size_One'), SS_FLASH_MSG_SUCCESS);
		} else {
			FlashMessage(sprintf(GetLang('Addon_splittest_Send_Step2_Size_Many'), $this->PrintNumber($send_size)), SS_FLASH_MSG_SUCCESS);
		}

		$flash_messages .= GetFlashMessages();

		$this->template_system->Assign('FlashMessages', $flash_messages, false);

		if (self::CheckCronEnabled()) {
			$this->template_system->Assign('CronEnabled', true);

			/**
			 * Get the sendstudio functions file to create the date/time box.
			 */
			require_once SENDSTUDIO_FUNCTION_DIRECTORY . '/sendstudio_functions.php';

			/**
			 * also need to load the 'send' language file so it can put in the names/descriptions.
			 */
			$ssf = new SendStudio_Functions;
			$ssf->LoadLanguageFile('send');
			$timebox = $ssf->CreateDateTimeBox(0, false, 'datetime', true);

			$this->template_system->Assign('ScheduleTimeBox', $timebox, false);
		}

		$this->template_system->Assign('ShowBounceInfo', $user->HasAccess('Lists', 'BounceSettings'));

		$this->template_system->Assign('DisplayEmbedImages', SENDSTUDIO_ALLOW_EMBEDIMAGES);
		$this->template_system->Assign('EmbedImagesByDefault', SENDSTUDIO_DEFAULT_EMBEDIMAGES);

		$list_api->Load($list_id);
		$details = [
			'fromname' => $list_api->Get('ownername'),
			'fromemail' => $list_api->Get('owneremail'),
			'replytoemail' => $list_api->Get('replytoemail'),
			'bounceemail' => $list_api->Get('bounceemail'),
		];

		$customfield_settings = [];
		$list_customfields = $list_api->GetCustomFields($listids);
		foreach ($list_customfields as $fieldid => $fielddetails) {
			if (strtolower($fielddetails['fieldtype']) != 'text') {
				continue;
			}
			$customfield_settings[$fieldid] = htmlspecialchars($fielddetails['name'], ENT_QUOTES, SENDSTUDIO_CHARSET);
		}

		$show_customfields = false;
		if (!empty($customfield_settings)) {
			$show_customfields = true;
		}

		$this->template_system->Assign('CustomFields', $customfield_settings);
		$this->template_system->Assign('ShowCustomFields', $show_customfields);

		foreach ($details as $name => $value) {
			$this->template_system->Assign($name, $value);
		}

		$this->template_system->Assign('AdminUrl', $this->admin_url, false);
		$this->template_system->ParseTemplate('send_step2');
	}

	/**
	 * 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 = [
			'sendfromname' => 'EnterSendFromName',
			'sendfromemail' => 'EnterSendFromEmail',
			'replytoemail' => 'EnterReplyToEmail',
		];

		$erors = [];
		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 (['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([$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('Splittest');
		$split_campaign_details = $api->Load($send_details['splitid']);

		$sendingCampaigns = [];
		$send_details['newsletters'] = [];

		$count = 0;
		foreach ($split_campaign_details['splittest_campaigns'] as $campaignid => $campaignname) {
			 $count++;

			 if ($count != count($split_campaign_details['splittest_campaigns']) ){
				$campaignname =$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 = [];

		switch ($send_details['sendingto']['sendtype']) {
			case 'list':
				require_once SENDSTUDIO_API_DIRECTORY . '/lists.php';
				$list_api = new Lists_API;
				$count = 0;
				foreach ($send_details['sendingto']['sendids'] as $listid) {
					$list_api->Load($listid);
					$count ++;
					if ($count != count($send_details['sendingto']['sendids'])){
						$send_list[] = htmlspecialchars($list_api->Get('name').', ', ENT_QUOTES, SENDSTUDIO_CHARSET);
					}else{
					    $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 = [];

		// 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);

		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 = ['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, [], 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'] = [];

			$statids = [];

			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');
	}

	/**
	 * Show_Send_Step_4
	 * Step 4 handles two pieces of functionality:
	 * - if cron support is enabled, it "approves" the job for sending and then redirects the user to the main splittest page
	 *
	 * If cron is not enabled, it processes and sends the emails out in popup mode.
	 * It looks at the queues table for people to send to, and sends one email per window refresh.
	 * It prints out a report of what's going on:
	 * - how many have been sent
	 * - how many left
	 * - approx how long it has taken so far
	 * - approx how long to go
	 * - optional extra - pause after displaying that info and sending the email (based on user restrictions)
	 *
	 * @uses Jobs_API
	 * @uses Jobs_API::ApproveJob
	 * @uses Jobs_API::QueueSize
	 * @uses CheckCronEnabled
	 * @uses Splittest_Send_API::StartJob
	 */
	public function Show_Send_Step_4()
	{
		$send_details = IEM::sessionGet('SplitTestSendDetails');

		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;
		}

		$jobid = $send_details['Job'];

		require_once SENDSTUDIO_API_DIRECTORY . '/jobs.php';
		$jobApi = new Jobs_API;

		if (isset($_GET['Start']) || self::CheckCronEnabled()) {
			/**
			 * Remove the "cleanup" variables so we don't kill the send off when we either
			 * - successfully schedule a send
			 * - or start a send going.
			 */
			IEM::sessionRemove('SplitTestSend_Cleanup');

			$user = IEM::getCurrentUser();
			$jobApi->ApproveJob($jobid, $user->Get('userid'), $user->Get('userid'));
		}

		/**
		 * If we get here and cron is enabled, we're finishing off a scheduled send setup.
		 * Show a message and return the user to the manage screen.
		 */
		if (self::CheckCronEnabled()) {
			FlashMessage(GetLang('Addon_splittest_Send_JobScheduled'), SS_FLASH_MSG_SUCCESS, $this->admin_url);
			return;
		}

		$this->template_system->Assign('AdminUrl', $this->admin_url, false);

		$send_api = $this->GetApi('Splittest_Send');

		if (isset($_GET['Start'])) {
			$send_api->StartJob($jobid, $send_details['splitid']);
		}

		$sendqueue = $jobApi->GetJobQueue($jobid);

		$job = $jobApi->LoadJob($jobid);

		$send_api->Set('statids', $send_details['Stats']);

		$send_api->Set('jobdetails', $job['jobdetails']);
		$send_api->Set('jobowner', $job['ownerid']);

		$queuesize = $jobApi->QueueSize($sendqueue, 'splittest');

		$send_details['SendQueue'] = $sendqueue;

		$timenow = $send_api->GetServerTime();

		$timediff = ($timenow - $send_details['SendStartTime']);

		$time_so_far = $this->TimeDifference($timediff);

		$num_left_to_send = $send_details['SendSize'] - $queuesize;

		if ($num_left_to_send > 0) {
			$timeunits = $timediff / ($num_left_to_send);
			$timediff = ($timeunits * $queuesize);
		} else {
			$timediff = 0;
		}
		$timewaiting = $this->TimeDifference($timediff);

		$this->template_system->Assign('SendTimeSoFar', sprintf(GetLang('Addon_splittest_Send_Step4_TimeSoFar'), $time_so_far));
		$this->template_system->Assign('SendTimeLeft', sprintf(GetLang('Addon_splittest_Send_Step4_TimeLeft'), $timewaiting));

		if ($num_left_to_send == 1) {
			$this->template_system->Assign('Send_NumberAlreadySent', GetLang('Addon_splittest_Send_Step4_NumberSent_One'));
		} else {
			$this->template_system->Assign('Send_NumberAlreadySent', sprintf(GetLang('Addon_splittest_Send_Step4_NumberSent_Many'), $this->PrintNumber($num_left_to_send)));
		}

		if ($queuesize <= 0) {
			require_once SENDSTUDIO_API_DIRECTORY . '/ss_email.php';
			$email = new SS_Email_API;

			if (SENDSTUDIO_SAFE_MODE) {
				$email->Set('imagedir', TEMP_DIRECTORY . '/send');
			} else {
				$email->Set('imagedir', TEMP_DIRECTORY . '/send.' . $jobid . '.' . $sendqueue);
			}
			$email->CleanupImages();

			$send_details['SendEndTime'] = $send_api->GetServerTime();
			IEM::sessionSet('SplitTestSendDetails', $send_details);

			$this->template_system->Assign('Send_NumberLeft', GetLang('Addon_splittest_Send_Step4_SendFinished'));
			$this->template_system->ParseTemplate('send_step4');
			?>
				<script>
					window.opener.focus();
					window.opener.document.location = '<?php echo $this->admin_url . '&Action=Send&Step=5'; ?>';
					window.close();
				</script>
			<?php
			return;
		}

		if ($queuesize == 1) {
			$this->template_system->Assign('Send_NumberLeft', GetLang('Addon_splittest_Send_Step4_NumberLeft_One'));
		} else {
			$this->template_system->Assign('Send_NumberLeft', sprintf(GetLang('Addon_splittest_Send_Step4_NumberLeft_Many'), $this->PrintNumber($queuesize)));
		}

		$send_api->SetupJob($jobid, $sendqueue);
		$send_api->SetupNewsletter();

		$recipients = $send_api->FetchFromQueue($sendqueue, 'splittest', 1, 1);

		$send_api->SetupDynamicContentFields($recipients);
		$send_api->SetupCustomFields($recipients);

		$sent_ok = false;

		foreach ($recipients as $p => $recipientid) {
			$send_results = $send_api->SendToRecipient($recipientid, $sendqueue);

			// save the info in the session, then see if we need to pause between each email.
			if ($send_results['success'] > 0) {
				$sent_ok = true;
				$send_details['EmailResults']['success']++;
			} else {
				$send_details['EmailResults']['failure']++;
			}
			$send_details['EmailResults']['total']++;
			IEM::sessionSet('SplitTestSendDetails', $send_details);
		}
		session_write_close();

		$this->template_system->ParseTemplate('send_step4');

		// we should only need to pause if we successfully sent.
		if ($sent_ok) {
			$send_api->Pause();
		}
	}

	/**
	 * Show_Send_Step_5
	 * Step 5 handles the final step of doing a send.
	 * It marks the stats as finished,
	 * it cleans up anything left over in the send queue
	 * Then it calls PrintSendFailureReport to show a report of how many emails were sent and how long it took.
	 *
	 * @uses Stats_API::MarkNewsletterFinished
	 * @uses PrintSendFailureReport
	 * @uses GetApi
	 * @uses Splittest_Send_API::FinishJob
	 * @uses Jobs_API::ClearQueue
	 */
	public function Show_Send_Step_5()
	{
		$send_details = IEM::sessionGet('SplitTestSendDetails');

		$this->template_system->Assign('AdminUrl', $this->admin_url, false);

		$jobid = $send_details['Job'];

		require_once SENDSTUDIO_API_DIRECTORY . '/stats.php';
		$statsapi = new Stats_API;

		/**
		 * Pass all of the stats through to the stats api.
		 *
		 * Since the stats contains an array of:
		 * newsletterid => statid
		 *
		 * we just need to pass through the statid's.
		 */
		$statsapi->MarkNewsletterFinished(array_values($send_details['Stats']), $send_details['SendSize']);

		$timetaken = $send_details['SendEndTime'] - $send_details['SendStartTime'];
		$timedifference = $this->TimeDifference($timetaken);

		$this->template_system->Assign('SendReport_Intro', sprintf(GetLang('Addon_splittest_Send_Step5_Intro'), $timedifference));

		$sendreport = '';
		if ($send_details['EmailResults']['success'] > 0) {
			if ($send_details['EmailResults']['success'] == 1) {
				FlashMessage(GetLang('Addon_splittest_Send_Step5_SendReport_Success_One'), SS_FLASH_MSG_SUCCESS);
			} else {
				FlashMessage(sprintf(GetLang('Addon_splittest_Send_Step5_SendReport_Success_Many'), $this->PrintNumber($send_details['EmailResults']['success'])), SS_FLASH_MSG_SUCCESS);
			}
			$sendreport = GetFlashMessages();
		}

		$this->PrintSendFailureReport($jobid, $sendreport);

		require_once SENDSTUDIO_API_DIRECTORY . '/jobs.php';
		$jobs_api = new Jobs_API;

		$send_api = $this->GetApi('Splittest_Send');
		$send_api->FinishJob($jobid, $send_details['splitid']);

		$jobs_api->ClearQueue($send_details['SendQueue'], 'splittest');

	}

	/**
	 * PrintSendFailureReport
	 * Prints out a report of
	 * - how many emails were sent,
	 * - how many emails were not sent (and why)
	 *
	 * Once that is worked out, it sets a template variable based on the report it creates.
	 *
	 * @param Int $job The job to load from the database with all of the job details in it
	 * @param String $sendreport The current "report" from the send (which would include how long the email took)
	 *
	 * @uses Jobs_API::LoadJob
	 * @uses Jobs_API::UnsentQueueSize
	 * @uses Jobs_API::Get_Unsent_Reasons
	 * @uses language/send.php
	 */
	private function PrintSendFailureReport($job=0, $sendreport='')
	{
		$send_details = IEM::sessionGet('SplitTestSendDetails');

		/**
		 * Include the 'send' language file
		 * as it contains all of the "failure report" error codes & messages.
		 */
		require_once SENDSTUDIO_LANGUAGE_DIRECTORY . '/default/send.php';

		require_once SENDSTUDIO_API_DIRECTORY . '/jobs.php';
		$jobApi = new Jobs_API;

		$jobinfo = $jobApi->LoadJob($job);

		$send_details = $jobinfo['jobdetails'];

		IEM::sessionSet('ReportQueue', $jobinfo['queueid']);

		$sendqueue = $jobinfo['queueid'];
		$failure_count = $jobApi->UnsentQueueSize($sendqueue);

		if ($failure_count > 0) {
			$error_report = '';
			$reason_codes = $jobApi->Get_Unsent_Reasons($jobinfo['queueid']);
			foreach ($reason_codes as $error_reason) {
				$this->template_system->Assign('ReportLink', sprintf(GetLang('SendReport_Failure_Link'), $error_reason['reasoncode']));
				$reason_message = GetLang('SendReport_Failure_Reason_' . $error_reason['reasoncode']);
				if ($error_reason['count'] == 1) {
					$this->template_system->Assign('ReasonMessage', sprintf(GetLang('SendReport_Failure_Reason_One'), $reason_message));
				} else {
					$this->template_system->Assign('ReasonMessage', sprintf(GetLang('SendReport_Failure_Reason_Many'), $this->PrintNumber($error_reason['count']), $reason_message));
				}
				$error_report .= $this->template_system->ParseTemplate('sendreport_failure_reason', true);
			}

			$error = '';

			if ($failure_count == 1) {
				$error = GetLang('SendReport_Failure_One');
			} else {
				$error = sprintf(GetLang('SendReport_Failure_Many'), $this->PrintNumber($failure_count));
			}
			$error .= '<br/><ul>' . $error_report . '</ul>';
			FlashMessage($error, SS_FLASH_MSG_ERROR);

			$sendreport .= GetFlashMessages();
		}

		$this->template_system->Assign('SendReport_Details', $sendreport, false);
		$this->template_system->ParseTemplate('sendreport');
	}


	/**
	 * Show_Send_Step_10
	 * This is the page that gets shown when a user clicks "pause" in the split test send popup window.
	 *
	 * It marks the job as "paused" in the database
	 * then shows an appropriate message.
	 *
	 * @uses GetApi
	 * @uses Splittest_Send_API::PauseJob
	 */
	public function Show_Send_Step_10()
	{
		$send_details = IEM::sessionGet('SplitTestSendDetails');
		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;
		}

		$job = (int)$send_details['Job'];

		/**
		 * Pause it in the split test.
		 * This makes it easier to work out a send's "state" (paused, in progress etc).
		 */
		$send_api = $this->GetApi('Splittest_Send');
		$paused = $send_api->PauseJob($job, $send_details['splitid']);

		if ($paused) {
			FlashMessage(GetLang('Addon_splittest_Send_Paused_Success'), SS_FLASH_MSG_SUCCESS);
		} else {
			FlashMessage(GetLang('Addon_splittest_Send_Paused_Failure'), SS_FLASH_MSG_ERROR);
		}

		$flash_messages = GetFlashMessages();

		$this->template_system->Assign('FlashMessages', $flash_messages, false);
		$this->template_system->Assign('AdminUrl', $this->admin_url);
		$this->template_system->ParseTemplate('send_paused');
	}

	/**
	 * Show_Send_Step_20
	 * Step 20 is what is shown if a user closes the popup sending window and
	 * has to 'Pause' the send before they can resume it.
	 *
	 * Basically it:
	 * - calls "PauseJob" in the splittest send api,
	 * - sets a flash message,
	 * - redirects the user back to the "Manage" page.
	 *
	 * If no id is supplied (or it's invalid), then an error is saved
	 * and the user is taken back to the manage page.
	 *
	 * @uses GetApi
	 * @uses SplitTest_API::Load
	 * @uses SplitTest_Send_API::PauseJob
	 *
	 * @return Void Returns nothing. Pauses the job, saves an appropriate flash message and takes the user back to the 'Manage' page.
	 */
	public function Show_Send_Step_20()
	{
		$splitid = 0;
		if (isset($_GET['id'])) {
			$splitid = (int)$_GET['id'];
		}
		if ($splitid <= 0) {
			FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		$split_api = $this->GetApi();
		$split_details = $split_api->Load($splitid);
		if (empty($split_details)) {
			FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		$send_api = $this->GetApi('SplitTest_Send');

		$paused = $send_api->PauseJob($split_details['jobid'], $split_details['splitid']);

		if ($paused) {
			FlashMessage(GetLang('Addon_splittest_Send_Paused_Success'), SS_FLASH_MSG_SUCCESS, $this->admin_url);
		} else {
			FlashMessage(GetLang('Addon_splittest_Send_Paused_Failure'), SS_FLASH_MSG_ERROR, $this->admin_url);
		}
	}

	/**
	 * Show_Send_Step_30
	 * This shows a summary report of the split test campaign
	 * after a user has paused the campaign
	 * and they want to resume sending it
	 *
	 * It shows:
	 * - which lists/segments it will be sent to
	 * - the split test name
	 * - which campaigns it will send
	 *
	 * and a "resume" button.
	 *
	 * If cron is enabled, then it will mark the job as "waiting" to send again in the database,
	 * set a flash message and redirect the user back to the "manage split tests" page.
	 *
	 * @uses GetApi
	 * @uses Splittest_API::Load
	 * @uses Jobs_API::LoadJob
	 * @uses CheckCronEnabled
	 * @uses Splittest_Send_API::ResumeJob
	 */
	public function Show_Send_Step_30()
	{
		$splitid = 0;
		if (isset($_GET['id'])) {
			$splitid = (int)$_GET['id'];
		}

		$api = $this->GetApi();
		$split_campaign_details = $api->Load($splitid);
		if (empty($split_campaign_details)) {
			FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		$jobid = 0;
		if (isset($split_campaign_details['jobid'])) {
			$jobid = (int)$split_campaign_details['jobid'];
		}

		require_once SENDSTUDIO_API_DIRECTORY . '/jobs.php';
		$jobApi = new Jobs_API;
		$job = $jobApi->LoadJob($jobid);

		if (empty($job)) {
			FlashMessage(GetLang('Addon_splittest_Send_InvalidSplitTest'), SS_FLASH_MSG_ERROR, $this->admin_url);
			return;
		}

		/**
		 * If we're sending via cron,
		 * then mark the job as "waiting" to send again
		 * and then show an appropriate message.
		 */
		if (self::CheckCronEnabled()) {
			$send_api = $this->GetApi('SplitTest_Send');
			$resumed = $send_api->ResumeJob($jobid, $splitid);
			if ($resumed) {
				FlashMessage(GetLang('Addon_splittest_Send_Resumed_Success'), SS_FLASH_MSG_SUCCESS, $this->admin_url);
			} else {
				FlashMessage(GetLang('Addon_splittest_Send_Resumed_Failure'), SS_FLASH_MSG_ERROR, $this->admin_url);
			}
			return;
		}

		$sendingCampaigns = [];
		$send_details['newsletters'] = [];
		foreach ($split_campaign_details['splittest_campaigns'] as $campaignid => $campaignname) {
			$sendingCampaigns[$campaignid] = htmlspecialchars($campaignname, ENT_QUOTES, SENDSTUDIO_CHARSET);
			$send_details['newsletters'][] = $campaignid;
		}

		$send_list = [];

		switch ($job['jobdetails']['sendingto']['sendtype']) {
			case 'list':
				require_once SENDSTUDIO_API_DIRECTORY . '/lists.php';
				$list_api = new Lists_API;
				foreach ($job['jobdetails']['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 ($job['jobdetails']['sendingto']['sendids'] as $segmentid) {
					$segment_api->Load($segmentid);
					$send_list[] = htmlspecialchars($segment_api->Get('segmentname'), ENT_QUOTES, SENDSTUDIO_CHARSET);
				}

				$this->template_system->Assign('SendingToSegments', true);
			break;
		}

		/**
		 * Set everything in the session ready to go.
		 */
		$job['jobdetails']['Job'] = $job['jobid'];
		IEM::sessionSet('SplitTestSendDetails', $job['jobdetails']);

		/**
		 * Work out how many more emails there are to send.
		 */
		$send_size = $job['jobdetails']['sendinfo']['sendsize_left'];

		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('sendingCampaigns', $sendingCampaigns);
		$this->template_system->Assign('sendLists', $send_list);

		$this->template_system->Assign('AdminUrl', $this->admin_url, false);
		$this->template_system->ParseTemplate('send_step3');
	}

	/**
	 * CheckCronEnabled
	 * Checks whether cron support is enabled in the app
	 * and also if split test cron support is not disabled.
	 *
	 * If cron is not enabled (or split test cron sending is disabled),
	 * it will return false.
	 *
	 * If cron is enabled and split test cron sending is enabled (for any timeframe),
	 * it will return true.
	 *
	 * @uses SENDSTUDIO_CRON_ENABLED
	 * @uses SENDSTUDIO_CRON_SPLITTEST
	 *
	 * @return Boolean Returns false if cron is not enabled or if split test cron support is disabled. If both are enabled, returns true.
	 */
	public static function CheckCronEnabled()
	{
		if (!defined('SENDSTUDIO_CRON_SPLITTEST')) {
			return false;
		}

		if (SENDSTUDIO_CRON_ENABLED && SENDSTUDIO_CRON_SPLITTEST > 0) {
			return true;
		}
		return false;
	}
}
