using System; using System.Data; using System.Collections; using Dynamicweb.Core; using Dynamicweb.Data; using Dynamicweb.Ecommerce.International; using Dynamicweb.Ecommerce.Orders; using Dynamicweb.Ecommerce.Shops; using Dynamicweb.Extensibility.AddIns; using Dynamicweb.Extensibility.Editors; using Dynamicweb.Frontend; using Dynamicweb.Mailing; using Dynamicweb.SystemTools; using Dynamicweb.Security.UserManagement; using Dynamicweb.Core.Helpers; namespace Dynamicweb.EmailMarketing.RecipientProviders.AbandonedCartProvider { /// /// Abandoned cart recipients provider /// [AddInName("Abandoned cart recipients")] public class AbandonedCartRecipientProvider : EmailRecipientProvider, IDropDownOptions { /// /// Gets or sets 'mark abandoned after' value /// /// Integer value of hours [AddInParameter("MarkAbandonedAfter"), AddInLabel("Mark abandoned after"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")] public int MarkAbandonedAfter { get; set; } = 180; // Default 180 = 3 hours /// /// Gets or sets 'ignore carts older than' value /// /// Integer value of hours [AddInParameter("IgnoreAbandonedCartsOlderThan"), AddInLabel("Ignore carts older than"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")] public int IgnoreAbandonedCartsOlderThan { get; set; } = 1440; // Default 1440 = 24 hours /// /// Gets or sets 'only abandoned carts from this shop' value /// /// String value [AddInParameter("AbandonedCartsShop"), AddInLabel("Only carts from this shop"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")] public string AbandonedCartsShop { get; set; } = string.Empty; /// /// Gets or sets 'only abandoned carts in this language' value /// /// String value [AddInParameter("AbandonedCartsLanguage"), AddInLabel("Only carts in this language"), AddInParameterEditor(typeof(DropDownParameterEditor), "NewGUI=true;none=false;SortBy=Key")] public string AbandonedCartsLanguage { get; set; } = string.Empty; /// /// Gets or sets 'require customer was logged in' value /// /// True in case when customer was logged in [AddInParameter("RequireCustomerLoggedIn"), AddInLabel("Require customer was logged in"), AddInParameterEditor(typeof(YesNoParameterEditor), "")] public bool RequireCustomerLoggedIn { get; set; } /// /// Gets or sets 'require customer accepted sales terms' value /// /// Boolean value [AddInParameter("RequireCustomerAcceptedSalesTerms"), AddInLabel("Require customer accepted sales terms"), AddInParameterEditor(typeof(YesNoParameterEditor), "")] public bool RequireCustomerAcceptedSalesTerms { get; set; } /// /// Gets or sets 'require logged in or accepted sales terms' value /// /// Boolean value [AddInParameter("RequireCustomerLoggedInOrAcceptedTerms"), AddInLabel("Require logged in or accepted sales terms"), AddInParameterEditor(typeof(YesNoParameterEditor), "")] public bool RequireEitherCustomerLoggedInOrAcceptedTerms { get; set; } /// /// Get order recipients /// /// Recipient collection public override RecipientCollection GetRecipients() { RecipientCollection recipients = new RecipientCollection(); CommandBuilder commandBuilder = new CommandBuilder(); commandBuilder.Add("SELECT OrderID, OrderCustomerEmail, OrderCustomerName, OrderCustomerAccessUserID"); commandBuilder.Add("FROM EcomOrders"); commandBuilder.Add("WHERE"); commandBuilder.Add(" OrderComplete = 0"); commandBuilder.Add(" AND OrderCart = 1"); commandBuilder.Add(" AND OrderModified " + GetOrderModifiedWhereClause(IgnoreAbandonedCartsOlderThan, MarkAbandonedAfter)); commandBuilder.Add(" AND OrderCustomerEmail IS NOT NULL"); commandBuilder.Add(" AND OrderCustomerEmail <> ''"); commandBuilder.Add(" AND OrderCustomerEmail <> ''"); //ensure that cart is not "lost" commandBuilder.Add(" AND (OrderCustomerAccessUserID IS NULL OR OrderCustomerAccessUserID < 1 OR OrderID = (SELECT TOP 1 AccessUserCartID FROM AccessUser WHERE AccessUserID = OrderCustomerAccessUserID)"); //also check contexts commandBuilder.Add(" OR OrderID IN (SELECT OrderContextAccessUserOrderID FROM EcomOrderContextAccessUserRelation WHERE OrderContextAccessUserAccessUserID = OrderCustomerAccessUserID))"); if (RequireEitherCustomerLoggedInOrAcceptedTerms || RequireCustomerLoggedIn || RequireCustomerAcceptedSalesTerms) { commandBuilder.Add(" AND ("); if (RequireEitherCustomerLoggedInOrAcceptedTerms || RequireCustomerLoggedIn) { commandBuilder.Add(" OrderCustomerAccessUserID > 0"); } if (RequireEitherCustomerLoggedInOrAcceptedTerms) { commandBuilder.Add(" OR"); } else if (RequireCustomerLoggedIn && RequireCustomerAcceptedSalesTerms) { commandBuilder.Add(" AND"); } if (RequireEitherCustomerLoggedInOrAcceptedTerms || RequireCustomerAcceptedSalesTerms) { commandBuilder.Add(" OrderCustomerAccepted = 1"); } commandBuilder.Add(" )"); } if (!string.IsNullOrWhiteSpace(AbandonedCartsShop)) { commandBuilder.Add(" AND OrderShopID = '" + AbandonedCartsShop + "'"); } if (!string.IsNullOrWhiteSpace(AbandonedCartsLanguage)) { commandBuilder.Add(" AND OrderLanguageID = '" + AbandonedCartsLanguage + "'"); } using (IDataReader dataReader = Database.CreateDataReader(commandBuilder)) { while (dataReader.Read()) { string orderId = Converter.ToString(dataReader["OrderID"]); string email = Converter.ToString(dataReader["OrderCustomerEmail"]); string name = Converter.ToString(dataReader["OrderCustomerName"]); int userId = Converter.ToInt32(dataReader["OrderCustomerAccessUserID"]); if (!StringHelper.IsValidEmailAddress(email)) { continue; } Recipient recipient = new Recipient(); recipient.EmailAddress = email; recipient.Name = name; recipient.RecipientKey = orderId; if (userId > 0) { User user = User.GetUserByID(userId); AccessUserRecipientProvider.SetTags(recipient, user); } recipients.Add(recipient); } } return recipients; } /// /// Gets context of recipient content /// /// Recipient object instance /// Page ciew context with order data public override PageViewContext GetRecipientContentContext(Recipient recipient) { PageViewContext pageViewContext = new PageViewContext(); Order order = Order.GetOrderById(recipient.RecipientKey); if (order != null && !string.IsNullOrEmpty(order.Id)) { pageViewContext.SetValue("OrderID", recipient.RecipientKey); pageViewContext.SetValue("Order", order); } return pageViewContext; } private static string GetOrderModifiedWhereClause(int lowerTimeBound, int upperTimeBound) { string clause = ""; DateTime now = DateTime.Now; string lower = Database.SqlDate(now.AddMinutes(-lowerTimeBound)); string upper = Database.SqlDate(now.AddMinutes(-upperTimeBound)); if (lowerTimeBound > 0 && upperTimeBound > 0) { clause = string.Format("BETWEEN {0} AND {1}", lower, upper); } else if (lowerTimeBound > 0) { clause = string.Format(">= {0}", lower); } else if (upperTimeBound > 0) { clause = string.Format("<= {0}", upper); } else { clause = "<= GETDATE()"; } return clause; } /// /// Implements IDropDownOptions.GetOptions method, /// and used for DropDownLists control /// /// Specified current control name /// Hashtable with specified control options public Hashtable GetOptions(string name) { Hashtable result = new Hashtable(); if (name == "MarkAbandonedAfter") { TimeSpan[] timePeriods = new TimeSpan[] { new TimeSpan(0, 1, 0), new TimeSpan(0, 30, 0), new TimeSpan(1, 0, 0), new TimeSpan(2, 0, 0), new TimeSpan(3, 0, 0), new TimeSpan(4, 0, 0), new TimeSpan(5, 0, 0), new TimeSpan(6, 0, 0), new TimeSpan(12, 0, 0), new TimeSpan(18, 0, 0), new TimeSpan(24, 0, 0), new TimeSpan(48, 0, 0), new TimeSpan(72, 0, 0), new TimeSpan(96, 0, 0), new TimeSpan(120, 0, 0), new TimeSpan(144, 0, 0), new TimeSpan(7, 0, 0, 0), new TimeSpan(14, 0, 0, 0, 0), new TimeSpan(30, 0, 0, 0, 0) }; foreach (TimeSpan span in timePeriods) { result.Add(Convert.ToInt32(span.TotalMinutes), GetTimeSpanInText(span)); } } else if (name == "IgnoreAbandonedCartsOlderThan") { result.Add(0, Translate.Translate("No limit")); for (var i = 1; i <= 48; i++) { var span = new TimeSpan(i, 0, 0); result.Add(Convert.ToInt32(span.TotalMinutes), GetTimeSpanInText(span)); } TimeSpan[] additionalTimePeriods = new TimeSpan[] { new TimeSpan(60, 0, 0), new TimeSpan(72, 0, 0), new TimeSpan(7, 0, 0, 0), new TimeSpan(14, 0, 0, 0, 0), new TimeSpan(30, 0, 0, 0, 0) }; foreach (TimeSpan span in additionalTimePeriods) { result.Add(Convert.ToInt32(span.TotalMinutes), GetTimeSpanInText(span)); } } else if (name == "AbandonedCartsShop") { result.Add("", Translate.Translate("All")); foreach (Shop shop in Shop.GetShops()) { result.Add(shop.Id, shop.Name); } } else if (name == "AbandonedCartsLanguage") { result.Add("", Translate.Translate("All")); foreach (Language language in Language.GetLanguages()) { result.Add(language.LanguageId, language.Name); } } return result; } private string GetTimeSpanInText(TimeSpan span) { string timeText = ""; int timeValue = 0; if (span.TotalDays >= 30) { timeText = "month" + (span.TotalDays > 30 ? "s" : string.Empty); timeValue = Convert.ToInt32(span.TotalDays) / 30; } else if (span.TotalDays >= 7) { timeText = "week" + (span.TotalDays > 7 ? "s" : string.Empty); timeValue = Convert.ToInt32(span.TotalDays) / 7; } else if (span.TotalHours >= 1) { timeText = "hour" + (span.TotalHours > 1 ? "s" : string.Empty); timeValue = Convert.ToInt32(span.TotalHours); } else if (span.TotalMinutes >= 1) { timeText = "minute" + (span.TotalMinutes > 1 ? "s" : string.Empty); timeValue = Convert.ToInt32(span.TotalMinutes); } else { throw new ArgumentException("TimeSpan 'span' is outside the excepted range."); } return string.Format("{0} {1}", timeValue, Translate.Translate(timeText)); } } }