using System; using System.Data; using System.Data.SqlTypes; using System.Web.UI; using System.Web.UI.WebControls; using System.Collections.Generic; using CMS.CMSHelper; using CMS.GlobalHelper; using CMS.Newsletter; using CMS.UIControls; using CMS.Scheduler; using CMS.SiteProvider; using CMS.SettingsProvider; public partial class CMSModules_Newsletters_Controls_SendVariantIssue : CMSAdminControl { #region "Constants" private const int STATE_WAITING_TO_SEND_WIZARD = 1; private const int STATE_WAITING_TO_SEND_PAGE = 2; private const int STATE_TEST_WAITING_TO_SEL_WINNER = 3; private const int STATE_TEST_READY_FOR_SENDING = 4; private const int STATE_TEST_FINISHED = 5; #endregion /// /// Control mode /// public enum SendControlMode { /// /// Control is used in wizard step /// Wizard = 0, /// /// Control is used on page /// Send = 1 } #region "Private variables" private IssueInfo parentIssue = null; private ABTestInfo abTest = null; #endregion #region "Properties" /// /// ID of newsletter issue that should be sent, required for template based newsletters. /// public int IssueID { get; set; } /// /// Gets or sets mode. Value changes control behaviour (wizard - control is on new issue wizard; /// send - control is on send page). /// public SendControlMode Mode { get; set; } /// /// Gets value that indicates whether sending issue is allowed or not. /// public bool SendingAllowed { get { return (CurrentState == STATE_WAITING_TO_SEND_PAGE) || (CurrentState == STATE_WAITING_TO_SEND_WIZARD); } } /// /// Flag - indicates if force reloaded is needed in the next reload. /// public bool ForceReloadNeeded { get; set; } /// /// Gets or sets current state of control ("waiting to send", "testing finished" etc.) /// private int CurrentState { get; set; } #endregion #region "Events" /// /// Occurs when info message needs to be redrawn. /// public event EventHandler OnChanged; #endregion #region "Page events" protected void Page_Load(object sender, EventArgs e) { if (!Visible || StopProcessing || (IssueID <= 0)) { return; } ReloadData(ForceReloadNeeded); } #endregion #region "Methods" /// /// Reloads control data. /// /// Indicates if force reload should be used public override void ReloadData(bool forceReload) { if (StopProcessing && !forceReload) { return; } if (ForceReloadNeeded) { forceReload = true; ForceReloadNeeded = false; } int parentIssueId = 0; parentIssue = IssueInfoProvider.GetOriginalIssue(IssueID); if (parentIssue != null) { parentIssueId = parentIssue.IssueID; } // Get A/B test configuration abTest = ABTestInfoProvider.GetABTestInfoForIssue(parentIssueId); if (abTest == null) { // Ensure A/B test object with default settings abTest = new ABTestInfo() { TestIssueID = parentIssueId, TestSizePercentage = 50, TestWinnerOption = ABTestWinnerSelectionEnum.OpenRate, TestSelectWinnerAfter = 60 }; ABTestInfoProvider.SetABTestInfo(abTest); } CurrentState = GetCurrentState(parentIssue); InitControls(CurrentState, forceReload); ucMailout.ParentIssueID = parentIssueId; ucMailout.ReloadData(forceReload); InfoMessage = GetInfoMessage(CurrentState, parentIssue, (abTest != null ? abTest.TestWinnerOption : ABTestWinnerSelectionEnum.OpenRate), GetPlannedMailoutTime(ucMailout.HighestMailoutTime)); // Init test group slider List variants = IssueHelper.GetIssueVariants(parentIssue, null); ucGroupSlider.Variants = variants; bool allVariantsSent = true; if (variants != null) { allVariantsSent = variants.TrueForAll(delegate(IssueABVariantItem item) { return item.IssueStatus == IssueStatusEnum.Finished; }); } if ((parentIssue.IssueStatus == IssueStatusEnum.Finished) || allVariantsSent) { // Issue was sent long time ago => get number of subscribers from issue properties instead of current number of subscribers int perVariantEmails = 1; if (abTest != null) { perVariantEmails = abTest.TestNumberPerVariantEmails; } ucGroupSlider.NumberOfTestSubscribers = ucGroupSlider.Variants.Count * perVariantEmails; if (parentIssue.IssueStatus == IssueStatusEnum.Finished) { // Issue was sent => get number of subscribers from number of sent issues ucGroupSlider.NumberOfSubscribers = parentIssue.IssueSentEmails; } else { // Only variants was sent => get current number of subscribers ucGroupSlider.NumberOfSubscribers = GetEmailAddressCount(parentIssue.IssueNewsletterID, parentIssue.IssueSiteID); } } else { ucGroupSlider.NumberOfSubscribers = GetEmailAddressCount(parentIssue.IssueNewsletterID, parentIssue.IssueSiteID); } if (forceReload || !ucGroupSlider.Enabled) { ucGroupSlider.CurrentSize = (abTest != null ? abTest.TestSizePercentage : 10); } ucGroupSlider.ReloadData(forceReload); ucWO_OnChange(this, EventArgs.Empty); } /// /// Collects data from controls and fills A/B test info. /// /// A/B test info private bool SaveABTestInfo(ABTestInfo abi) { if (abi == null) { return false; } bool result = (abi.TestWinnerOption != ucWO.WinnerSelection) || (abi.TestSelectWinnerAfter != ucWO.TimeInterval) || (abi.TestSizePercentage != ucGroupSlider.CurrentSize); switch (Mode) { case SendControlMode.Wizard: case SendControlMode.Send: abi.TestWinnerOption = ucWO.WinnerSelection; abi.TestSelectWinnerAfter = ucWO.TimeInterval; break; } abi.TestSizePercentage = ucGroupSlider.CurrentSize; return result; } /// /// Initializes controls /// /// Current state of the control (controls are initializes according this value) /// Indicates if force data reload should be performed protected void InitControls(int currState, bool forceReload) { switch (currState) { case STATE_TEST_FINISHED: ucGroupSlider.Enabled = false; ucMailout.ShowSelectWinnerAction = false; ucMailout.ShowOpenedEmails = true; ucMailout.ShowUniqueClicks = true; ucMailout.ShowIssueStatus = true; ucMailout.ShowSelectionColumn = false; ucMailout.EnableMailoutTimeSetting = false; InitWinnerOption(false, abTest, forceReload); lblAdditionalInfo.Visible = true; lblAdditionalInfo.Text = GetString("newsletterissue_send.variantsendingfinished"); break; case STATE_TEST_READY_FOR_SENDING: ucGroupSlider.Enabled = true; ucMailout.ShowSelectWinnerAction = false; ucMailout.ShowOpenedEmails = true; ucMailout.ShowUniqueClicks = true; ucMailout.ShowIssueStatus = true; ucMailout.ShowSelectionColumn = true; ucMailout.EnableMailoutTimeSetting = true; ucMailout.OnChanged -= new EventHandler(ucMailout_OnChanged); ucMailout.OnChanged += new EventHandler(ucMailout_OnChanged); InitWinnerOption(true, abTest, forceReload); lblAdditionalInfo.Visible = true; lblAdditionalInfo.Text = GetString("newsletterissue_send.infovariantsending"); break; case STATE_TEST_WAITING_TO_SEL_WINNER: ucGroupSlider.Enabled = false; ucMailout.ShowSelectWinnerAction = true; ucMailout.ShowOpenedEmails = true; ucMailout.ShowUniqueClicks = true; ucMailout.ShowIssueStatus = true; ucMailout.ShowSelectionColumn = false; ucMailout.EnableMailoutTimeSetting = true; ucMailout.OnChanged -= new EventHandler(ucMailout_OnChanged); ucMailout.OnChanged += new EventHandler(ucMailout_OnChanged); InitWinnerOption(true, abTest, forceReload); lblAdditionalInfo.Visible = true; lblAdditionalInfo.Text = GetString("newsletterissue_send.infowaitingtoselwinner"); break; case STATE_WAITING_TO_SEND_PAGE: ucMailout.UseGroupingText = true; ucGroupSlider.Enabled = true; ucMailout.OnChanged -= new EventHandler(ucMailout_OnChanged); ucMailout.OnChanged += new EventHandler(ucMailout_OnChanged); InitWinnerOption(true, abTest, forceReload); lblAdditionalInfo.Visible = false; break; case STATE_WAITING_TO_SEND_WIZARD: ucMailout.UseGroupingText = true; ucGroupSlider.Enabled = true; ucMailout.OnChanged -= new EventHandler(ucMailout_OnChanged); ucMailout.OnChanged += new EventHandler(ucMailout_OnChanged); InitWinnerOption(true, abTest, forceReload); lblAdditionalInfo.Visible = false; break; } } protected void ucMailout_OnChanged(object sender, EventArgs e) { InfoMessage = GetInfoMessage(CurrentState, parentIssue, abTest.TestWinnerOption, GetPlannedMailoutTime(ucMailout.HighestMailoutTime)); ucWO_OnChange(this, EventArgs.Empty); if (OnChanged != null) { OnChanged(this, EventArgs.Empty); } } /// /// Returns information message according to current state, issue and A/B test. /// /// Current state /// Issue /// Winner option private string GetInfoMessage(int currState, IssueInfo issue, ABTestWinnerSelectionEnum winnerOption, DateTime plannedMailoutTime) { if (issue == null) { return null; } switch (currState) { case STATE_WAITING_TO_SEND_WIZARD: return null; case STATE_WAITING_TO_SEND_PAGE: return GetString("Newsletter_Issue_Header.NotSentYet"); case STATE_TEST_WAITING_TO_SEL_WINNER: switch (winnerOption) { case ABTestWinnerSelectionEnum.Manual: if (issue.IssueMailoutTime > DateTime.Now) { return String.Format(GetString("newsletterinfo.issuesentwaitingtosentwinner"), issue.IssueMailoutTime, (abTest != null ? abTest.TestWinnerSelected.ToString() : GetString("general.na"))); } else { return String.Format(GetString("newsletterinfo.issuesentwaitingtoselwinnermanually"), issue.IssueMailoutTime); } case ABTestWinnerSelectionEnum.OpenRate: return String.Format(GetString("newsletterinfo.issuesentwaitingtoselwinneropen"), issue.IssueMailoutTime, plannedMailoutTime); case ABTestWinnerSelectionEnum.TotalUniqueClicks: return String.Format(GetString("newsletterinfo.issuesentwaitingtoselwinnerclicks"), issue.IssueMailoutTime, plannedMailoutTime); } break; case STATE_TEST_READY_FOR_SENDING: return String.Format(GetString("newsletter_issue_header.issuesending"), plannedMailoutTime); case STATE_TEST_FINISHED: switch (winnerOption) { case ABTestWinnerSelectionEnum.Manual: return String.Format(GetString("newsletterinfo.issuesentwinnerselmanually"), issue.IssueMailoutTime, (abTest != null ? abTest.TestWinnerSelected.ToString() : GetString("general.na"))); case ABTestWinnerSelectionEnum.OpenRate: return String.Format(GetString("newsletterinfo.issuesentwinnerselopen"), (abTest != null ? abTest.TestWinnerSelected.ToString() : GetString("general.na"))); case ABTestWinnerSelectionEnum.TotalUniqueClicks: return String.Format(GetString("newsletterinfo.issuesentwinnerselclicks"), (abTest != null ? abTest.TestWinnerSelected.ToString() : GetString("general.na"))); } break; } return null; } /// /// Initialize winner option control. /// /// Control state (enabled/disabled) /// A-B test info to load to control /// TRUE if force load should be performed private void InitWinnerOption(bool enable, ABTestInfo abi, bool forceReload) { // Register handlers ucWO.OnChange -= new EventHandler(ucWO_OnChange); ucWO.OnChange += new EventHandler(ucWO_OnChange); if (abi == null) { return; } ucWO.Visible = true; ucWO.Enabled = enable; if (forceReload) { ucWO.WinnerSelection = abi.TestWinnerOption; ucWO.TimeInterval = abi.TestSelectWinnerAfter; } } protected void ucWO_OnChange(object sender, EventArgs e) { bool visible = (Mode == SendControlMode.Wizard) && ucWO.Enabled; lblWillBeSent.Visible = visible; if (visible) { bool manualWinner = false; manualWinner = (ucWO.WinnerSelection == ABTestWinnerSelectionEnum.Manual); if (manualWinner) { lblWillBeSent.Text = GetString("newsletterissue_send.winnerwillbesentmanually"); } else { lblWillBeSent.Text = String.Format(GetString("newsletterissue_send.winnerwillbesenton"), GetPlannedMailoutTime(ucMailout.HighestMailoutTime)); } } } /// /// Calculates planned mail out time. /// private DateTime GetPlannedMailoutTime(DateTime highestMailoutTime) { if (highestMailoutTime == DateTimeHelper.ZERO_TIME) { highestMailoutTime = DateTime.Now; } if (Mode == SendControlMode.Send) { if ((abTest != null) && (abTest.TestWinnerOption != ABTestWinnerSelectionEnum.Manual)) { return highestMailoutTime.AddMinutes(abTest.TestSelectWinnerAfter); } } else { if (ucWO.WinnerSelection != ABTestWinnerSelectionEnum.Manual) { return highestMailoutTime.AddMinutes(ucWO.TimeInterval); } } return highestMailoutTime; } /// /// "Sends" variant issue (enables all scheduled task associated to each variant). /// public bool SendIssue() { // Check current state before sending switch (CurrentState) { case STATE_TEST_READY_FOR_SENDING: ErrorMessage = GetString("newsletterissue_send.sendissuereadytobesent"); return false; case STATE_TEST_WAITING_TO_SEL_WINNER: case STATE_TEST_FINISHED: ErrorMessage = GetString("newsletterissue_send.sendissuehasbeensent"); return false; } if (!SaveIssue()) { return false; } // Enable scheduled tasks and set 'Ready for sending' status to all variants IssueHelper.EnableVariantScheduledTasks(parentIssue); return true; } /// /// Saves current newsletter settings. /// public bool SaveIssue() { try { switch (CurrentState) { case STATE_WAITING_TO_SEND_PAGE: case STATE_WAITING_TO_SEND_WIZARD: case STATE_TEST_READY_FOR_SENDING: case STATE_TEST_WAITING_TO_SEL_WINNER: if (abTest == null) { abTest = ABTestInfoProvider.GetABTestInfoForIssue(parentIssue.IssueID); } // Get A/B test settings from controls bool abTestChanged = SaveABTestInfo(abTest); if (abTest == null) { return false; } if (abTest.TestWinnerOption != ABTestWinnerSelectionEnum.Manual) { // Check minimal time interval if (abTest.TestSelectWinnerAfter < 5) { ErrorMessage = GetString("newsletterissue_send.saveissuewrongwinnerselectioninterval"); return false; } } // Check if test options has changed if (abTestChanged) { if (abTest.TestWinnerIssueID > 0) { // Options has been changed => reset previously selected winner IssueInfoProvider.DeleteMailoutTask(parentIssue.IssueGUID, parentIssue.IssueSiteID); abTest.TestWinnerIssueID = 0; abTest.TestWinnerSelected = DateTimeHelper.ZERO_TIME; // Hide/reload winner selection in issue mail-out grid ucMailout.ReloadData(false); } IssueInfoProvider.SetIssueInfo(parentIssue); } ABTestInfoProvider.SetABTestInfo(abTest); if (CurrentState == STATE_TEST_WAITING_TO_SEL_WINNER) { IssueHelper.EnsureWinnerSelectionTask(abTest, parentIssue, true, ucMailout.HighestMailoutTime); } // Update info message for parent control int currentState = GetCurrentState(parentIssue); InfoMessage = GetInfoMessage(currentState, parentIssue, abTest.TestWinnerOption, GetPlannedMailoutTime(ucMailout.HighestMailoutTime)); return true; case STATE_TEST_FINISHED: ErrorMessage = GetString("newsletterissue_send.saveissuehasbeensent"); break; } } catch (Exception e) { ErrorMessage = e.Message; return false; } return true; } /// /// Determines current state of newsletter A/B test. /// /// Parent issue private int GetCurrentState(IssueInfo parentIssue) { int currentState = 0; if (parentIssue == null) { return currentState; } switch (parentIssue.IssueStatus) { case IssueStatusEnum.Idle: if (Mode == SendControlMode.Send) { currentState = STATE_WAITING_TO_SEND_PAGE; } else { currentState = STATE_WAITING_TO_SEND_WIZARD; } break; case IssueStatusEnum.ReadyForSending: currentState = STATE_TEST_READY_FOR_SENDING; break; case IssueStatusEnum.TestPhase: currentState = STATE_TEST_WAITING_TO_SEL_WINNER; break; case IssueStatusEnum.PreparingData: case IssueStatusEnum.Sending: case IssueStatusEnum.Finished: currentState = STATE_TEST_FINISHED; break; } return currentState; } /// /// Returns all e-mail addresses for specified newsletter. /// /// Newsletter /// Site private int GetEmailAddressCount(int newsletterId, int siteId) { // Get e-mail addresses for ordinary subscriber, user subriber and role subscriber string siteName = SiteInfoProvider.GetSiteName(siteId); int emailAddresses = NewsletterInfoProvider.GetEmailAddressCount(newsletterId, siteName); DataSet ContactID = SubscriberNewsletterInfoProvider.GetSubscriptions( String.Format("NewsletterID={0} AND (SubscriptionApproved=1 OR SubscriptionApproved IS NULL) AND (SubscriptionEnabled=1 OR SubscriptionEnabled IS NULL) AND SubscriberType='om.contactgroup'", newsletterId), null, 0, "DISTINCT(SubscriberRelatedID) AS SubscriberRelatedID"); if (!DataHelper.DataSourceIsEmpty(ContactID)) { int groupId = 0; foreach (DataRow dr in ContactID.Tables[0].Rows) { groupId = ValidationHelper.GetInteger(dr[0], 0); DataSet dsMembers = ModuleCommands.OnlineMarketingGetContactGroupMemberIDs(groupId, 0, 0, NewsletterHelper.MonitorBouncedEmails(siteName), NewsletterHelper.BouncedEmailsLimit(siteName), newsletterId); if (!DataHelper.DataSourceIsEmpty(dsMembers)) { emailAddresses += ValidationHelper.GetInteger(dsMembers.Tables[0].Rows.Count, 0); } } } return emailAddresses; } #endregion }