/// <summary> /// Creates a mail info object based on information on a page. /// </summary> /// <returns></returns> public virtual MailInformation GetMailInformation(PageData mailPage) { // Get Meta Information MailInformation mailInfo = GetMailMetaData(mailPage); // Get the two body versions string mailBody = GetPageHtml(mailPage); string hostUrl = GetSiteUrl(mailPage); mailInfo.BodyHtml = RewriteUrls(mailBody, hostUrl); mailInfo.BodyText = QuickCleanMailText(GetPageText(mailPage)); // Let page add custom properties IPopulateCustomProperties customPropertiesProvider = mailPage as IPopulateCustomProperties; if (customPropertiesProvider != null) { // Let page populate the custom properties collection customPropertiesProvider.AddCustomProperties(mailInfo.CustomProperties); } return(mailInfo); }
/// <summary> /// Creates the mail info object with only meta information like subject /// sender etc. Does not add mail content to the object. /// </summary> public virtual MailInformation GetMailMetaData(PageData mailPage) { // Get content from page MailInformation mailInfo = new MailInformation(); mailInfo.PageLink = mailPage.PageLink; mailInfo.Subject = GetMailSubject(mailPage); if (mailPage["BaseUrl"] != null) { mailInfo.BaseUrl = mailPage["BaseUrl"].ToString(); } else { mailInfo.BaseUrl = GetSiteUrl(mailPage); } // The pagename can be used for other things later on mailInfo.PageName = mailInfo.PageName; // Sender address mailInfo.From = GetMailSender(mailPage); // Campaign data mailInfo.Utm.Campaign = GetCampaign(mailPage); return(mailInfo); }
/// <summary> /// Populates needed mail information before sending. Will also check /// for Mailgun specific properties /// </summary> /// <remarks> /// Note! Mailgun validate campaign codes, and if the campaign does /// not exist, it will be ignored (the email will still be sent) /// Note! Tags are limited to a count of 200 /// </remarks> /// <param name="mailPage"></param> /// <returns></returns> public override MailInformation GetMailInformation(PageData mailPage) { MailInformation mailInformation = base.GetMailInformation(mailPage); IPopulateCustomProperties customPropertiesProvider = mailPage as IPopulateCustomProperties; if (customPropertiesProvider == null) { // The base class will add custom properties if the page type // implements that - if NOT, we'll try to add them ourselves // by looking for special property names relevant to Mailgun var campaign = mailPage[MAILGUN_CAMPAIGN_PROPERTYNAME]; if (campaign != null) { mailInformation.Utm.Campaign = campaign.ToString(); } // Since the Utm Campaign can be set independently, we check if it // is set, and use it as a Mailgun campaign too if (string.IsNullOrEmpty(mailInformation.Utm.Campaign) == false) { mailInformation.CustomProperties.Add("o:campaign", mailInformation.Utm.Campaign); } if (mailPage[MAILGUN_TAG_PROPERTYNAME] != null) { mailInformation.CustomProperties.Add("o:tag", mailPage[MAILGUN_TAG_PROPERTYNAME]); } } return(mailInformation); }
protected MailInformation GetMailInformation(MailSenderBase sender, ContentReference pageRef, string from, string subject) { // Get content to send MailInformation mailInfo = sender.GetMailInformation(pageRef as PageReference); if (!string.IsNullOrEmpty(subject)) { mailInfo.Subject = subject; // Can be something other than the pagename } mailInfo.From = from; return(mailInfo); }
public MailInformation GetHtmlReport(string from, string subject, bool ShowSuccessAddresses) { if (from == null) { throw new ArgumentNullException("from", "From email address cannot be null"); } MailInformation mailInfo = new MailInformation(); mailInfo.Subject = subject; mailInfo.From = from; System.Text.StringBuilder reportMailBody = new System.Text.StringBuilder(); reportMailBody.AppendLine("<div id=\"MailReport\">"); // Start send reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">Started send:</span> <span class=\"onelinesectionvalue\">{0}</span></span></div>", this.SendStart.ToString()); // Start send reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">End send:</span> <span class=\"onelinesectionvalue\">{0}</span></div>", this.SendStop.ToString()); // Duration TimeSpan duration = TimeSpan.FromMilliseconds((double)this.TotalDuration); reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">Duration:</span> <span class=\"onelinesectionvalue\">{0}hr {1}min {2}sec</span></div>", duration.Hours.ToString(), duration.Minutes.ToString(), duration.Seconds.ToString()); // Number of emails sent reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">Number of emails sent:</span> <span class=\"onelinesectionvalue\">{0}</span></div>", this.NumberOfEmailsSent.ToString()); // Exceptions first if (ErrorMessages.Count > 0) { AppendMultiLineSectionAsHtml("Exceptions", ErrorMessages, reportMailBody); } // Warnings next if (WarningMessages.Count > 0) { AppendMultiLineSectionAsHtml("Warnings", WarningMessages, reportMailBody); } // Successfull HTML Text Recipients if (ShowSuccessAddresses == true) { AppendMultiLineSectionAsHtml("Successfull HTML Format Recipients", SuccessMessages, reportMailBody); } reportMailBody.AppendLine("</div>"); mailInfo.BodyHtml = reportMailBody.ToString(); return(mailInfo); }
/// <summary> /// Sends the mail batch using the SendGrid API /// </summary> /// <param name="mail">The mail.</param> /// <param name="recipients">The recipients.</param> /// <param name="onlyTestDontSendMail">if set to <c>true</c> [only test dont send mail].</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public override bool SendMailBatch(MailInformation mail, IEnumerable<JobWorkItem> recipients, bool onlyTestDontSendMail) { var settings = GetSettings(); if (recipients == null || recipients.Any() == false) throw new ArgumentException("No workitems", "recipients"); if (recipients.Count() > 1000) throw new ArgumentOutOfRangeException("recipients", "SendGrid supports maximum 1000 recipients per batch send."); var msg = new SendGridMessage(); msg.From = new MailAddress(mail.From); msg.Subject = mail.Subject; msg.Html = mail.BodyHtml; msg.Text = mail.BodyText; // Add recipinets to header, to hide other recipients in to field. List<string> addresses = recipients.Select(r => r.EmailAddress).ToList(); msg.Header.SetTo(addresses); msg.AddSubstitution("%recipient%", addresses); // To send message we need to have a to address, set that to from msg.To = new MailAddress[] { msg.From }; if (mail.EnableTracking) { // true indicates that links in plain text portions of the email // should also be overwritten for link tracking purposes. msg.EnableClickTracking(true); msg.EnableOpenTracking(); } if(mail.CustomProperties.ContainsKey("SendGridCategory")) { string category = mail.CustomProperties["SendGridCategory"] as string; if (string.IsNullOrEmpty(category) == false) msg.SetCategory(category); } var credentials = new NetworkCredential(settings.Username, settings.Password); // Create an Web transport for sending email. var transportWeb = new Web(credentials); transportWeb.Deliver(msg); return true; }
/// <summary> /// Send mail to mailreceivers /// </summary> /// <param name="subject">subject for mail</param> /// <param name="pageRef">reference to mailpage</param> /// <param name="from">from-address for mail</param> /// <param name="onlyTestDontSendMail">No mails are sent, generating report for user</param> /// <param name="job">The job to send as newsletter</param> /// <returns></returns> public SendMailLog SendNewsletter(string subject, string from, ContentReference pageRef, JobWorkItems workItems, bool onlyTestDontSendMail) { // Construct correct sender object based on config setting MailSenderBase sender = GetMailSender(); // Construct mail object with default values and content MailInformation mailInfo = GetMailInformation(sender, pageRef, from, subject); // Send it SendMailLog log; log = sender.SendEmail(mailInfo, workItems, onlyTestDontSendMail); // Add additional information to the log log.Subject = subject; return(log); }
/// <summary> /// Send mail to mailreceivers using Mailgun REST API /// </summary> /// <param name="mailInfo"></param> /// <param name="recepients">receivers</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A html formatted report of the send process</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode) { _log.Debug("Starting Mailgun Send"); // Inline all css InlineResult cssInline = PreMailer.Net.PreMailer.MoveCssInline(mailInfo.BodyHtml); mailInfo.BodyHtml = cssInline.Html; // Base will send SendMailLog log = base.SendEmail(mailInfo, recepients, testMode); // Log any messages, debug is only detected // if we have an HttpContext. if (IsInDebugMode()) { log.WarningMessages.Add("Premailer CSS warning messages are only shown in debug mode. Primarily for developers."); log.WarningMessages.AddRange(cssInline.Warnings.ToArray()); } _log.Debug("Ending Mailgun Send"); // return report return(log); }
public override bool SendMailBatch(MailInformation mail, IEnumerable<JobWorkItem> recipients, bool onlyTestDontSendMail) { MailgunSettings settings = GetSettings(); // ReSharper disable PossibleMultipleEnumeration if(recipients == null || recipients.Count() == 0) throw new ArgumentException("No workitems", "recipients"); if(recipients.Count() > 1000) throw new ArgumentOutOfRangeException("recipients", "Mailgun supports maximum 1000 recipients per batch send."); RestClient client = new RestClient(); client.BaseUrl = new Uri("https://api.mailgun.net/v2"); client.Authenticator = new HttpBasicAuthenticator("api", settings.ApiKey); if(string.IsNullOrEmpty(settings.ProxyAddress) == false) { client.Proxy = new WebProxy(settings.ProxyAddress, settings.ProxyPort); // Makes it easy to debug as Fiddler will show us the requests } RestRequest request = new RestRequest(); request.AlwaysMultipartFormData = true; request.AddParameter("domain", settings.Domain, ParameterType.UrlSegment); request.Resource = "{domain}/messages"; request.AddParameter("from", mail.From); request.AddParameter("subject", mail.Subject); request.AddParameter("text", mail.BodyText); request.AddParameter("html", mail.BodyHtml); if(mail.EnableTracking) { request.AddParameter("o:tracking", mail.EnableTracking); request.AddParameter("o:tracking-clicks", mail.EnableTracking); request.AddParameter("o:tracking-opens", mail.EnableTracking); } foreach (KeyValuePair<string, object> customProperty in mail.CustomProperties) { request.AddParameter(customProperty.Key, customProperty.Value.ToString()); } // Add custom data about job if (mail.CustomProperties.ContainsKey("v:newsletter-data") == false) { request.AddParameter("v:newsletter-data", string.Format("{{\"id\": \"{0}\", \"page\": \"{1}\" }}", recipients.First().JobId, mail.PageLink.ID)); } // Add all recipients StringBuilder recipVariables = new StringBuilder(); bool first = true; foreach (JobWorkItem recipient in recipients) { request.AddParameter("to", recipient.EmailAddress); if (first == false) { recipVariables.Append(","); } first = false; recipVariables.AppendFormat("\"{0}\" : {{\"id\": \"{1}\" }}", recipient.EmailAddress, recipient.GetHashCode()); } request.AddParameter("recipient-variables", "{" + recipVariables.ToString() + "}"); //if(onlyTestDontSendMail) //{ // request.AddParameter("o:testmode", true); //} request.Method = Method.POST; var response = client.Execute(request); Dictionary<string, string> resultParams = null; try { resultParams = new JsonDeserializer().Deserialize<Dictionary<string, string>>(response); } catch (Exception e) { _log.Warning("Unable to parse Mailgun response.", e); } if(response.StatusCode == HttpStatusCode.OK) { _log.Debug("Mailgun responded with: {0} - {1}", response.StatusCode, response.StatusDescription); if(string.IsNullOrEmpty(response.ErrorMessage) == false) { _log.Error("Response Error: {0}", response.ErrorMessage); } _log.Debug(response.Content); // json looks like: //{ // "message": "Queued. Thank you.", // "id": "<*****@*****.**>" //} // Update all recipients with information if(resultParams != null) { string info = resultParams["id"]; foreach (JobWorkItem recipient in recipients) { recipient.Info = info; } } } else { _log.Debug("Mailgun responded with: {0} - {1}", response.StatusCode, response.StatusDescription); string errorMessage = response.StatusDescription; if(resultParams != null) errorMessage = resultParams["message"]; if (string.IsNullOrEmpty(response.ErrorMessage) == false) { _log.Error("Response Error: {0}", response.ErrorMessage); } _log.Debug(response.Content); throw new HttpException((int)response.StatusCode, errorMessage); } return true; // ReSharper restore PossibleMultipleEnumeration }
public abstract bool SendMailBatch(MailInformation mail, IEnumerable <JobWorkItem> recipients, bool onlyTestDontSendMail);
/// <summary> /// Send mail to mailreceivers using System.Net.Mail /// </summary> /// <param name="mailInfo"></param> /// <param name="recepients">receivers</param> /// <param name="from">from-address for mail</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A html formatted report of the send process</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode) { _log.Debug("Starting Send"); // for logging SendMailLog log = new SendMailLog(); log.SendStart = DateTime.Now; // HttpContext.Current.Server.ScriptTimeout = 7200; // Need someone to send to if (recepients.Items.Count == 0) { _log.Error("Trying to send newsletter with an empty JobWorkCollection. Please check the collection before attemting to send mail."); throw new ArgumentNullException("recepients", "Recipient collection is empty, there is no recipients to send to."); } // And, we need a sender address if (string.IsNullOrEmpty(mailInfo.From)) { _log.Error("Missing from address. SMTP servers do not allow sending without a sender."); throw new ArgumentNullException("mailInfo", "Missing from address. SMTP servers do not allow sending without a sender."); } // Loop through receivers collection, send email for each foreach (JobWorkItem recipient in recepients) { _log.Debug(string.Format("Job {0}, Email: {1}, Status: {2}", recipient.JobId.ToString(), recipient.EmailAddress, recipient.Status.ToString())); MailMessage mail = new MailMessage(mailInfo.From, recipient.EmailAddress); mail.Subject = mailInfo.Subject; mail.Body = mailInfo.BodyHtml; mail.IsBodyHtml = true; try { if (SendMail(mail, testMode)) { log.SuccessMessages.Add(recipient.EmailAddress); // Update status and save it recipient.Status = JobWorkStatus.Complete; // Only save if real work item (could be a test item) if (recipient.JobId > 0) { recipient.Save(); } } } catch (Exception ex) { _log.Error(string.Format("Error sending to email: {0}", recipient.EmailAddress), ex); string exceptionMsg = string.Format("Email: {0}\r\nException: {1}\r\n\r\n", recipient.EmailAddress, ex.Message); log.ErrorMessages.Add(exceptionMsg); // Update work item recipient.Status = JobWorkStatus.Failed; if (exceptionMsg.Length >= 2000) { exceptionMsg = exceptionMsg.Substring(0, 1999); } recipient.Info = exceptionMsg; if (recipient.JobId > 0) { recipient.Save(); } } } // Finished log.SendStop = DateTime.Now; _log.Debug("Ending Send"); // return report return(log); }
/// <summary> /// Send mail to mailreceivers using System.Web.Mail /// </summary> /// <param name="mailInfo">A MailInformation object with the content to send</param> /// <param name="recepients">receivers</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A report of the send process</returns> public abstract SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode);
/// <summary> /// /// </summary> /// <param name="mailInfo"></param> /// <param name="recepients">receivers</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A html formatted report of the send process</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode) { _log.Debug("Starting Send"); // for logging SendMailLog log = new SendMailLog(); // Need someone to send to if (recepients.Items.Count == 0) { _log.Error("Trying to send newsletter with an empty JobWorkCollection. Please check the collection before attemting to send mail."); throw new ArgumentNullException("recepients", "Recipient collection is empty, there is no recipients to send to."); } // And, we need a sender address if (string.IsNullOrEmpty(mailInfo.From)) { _log.Error("Missing from address."); throw new ArgumentNullException("mailInfo", "Missing from address."); } // Inline all css PreMailer.Net.PreMailer preMailer = new PreMailer.Net.PreMailer(mailInfo.BodyHtml); if (mailInfo.Utm.HasValidUtmCode) { preMailer.AddAnalyticsTags(mailInfo.Utm.Source, mailInfo.Utm.Medium, mailInfo.Utm.Campaign, mailInfo.Utm.Content); } InlineResult cssInline = preMailer.MoveCssInline(); mailInfo.BodyHtml = cssInline.Html; // Log any messages, debug is only detected // if we have an HttpContext. if (IsInDebugMode()) { log.WarningMessages.Add("Premailer CSS warning messages are only shown in debug mode. Primarily for developers."); log.WarningMessages.AddRange(cssInline.Warnings.ToArray()); } // Loop through receivers collection, add to collection and send // one email per batch size. int batchRun = 0; int batchSize = GetBatchSize(); do { IEnumerable <JobWorkItem> workItems = recepients.Skip(batchRun * batchSize).Take(batchSize); int numberofItemsToSend = workItems.Count(); if (numberofItemsToSend == 0) { break; } batchRun++; try { if (SendMailBatch(mailInfo, workItems, testMode)) { // Mark each item as sent foreach (JobWorkItem workItem in workItems) { log.SuccessMessages.Add(workItem.EmailAddress); // Update status and save it workItem.Status = JobWorkStatus.Complete; // Only save if real work item (could be a test item) if (workItem.JobId > 0) { workItem.Save(); } } } } catch (Exception ex) { _log.Error(string.Format("Error sending batch (to {0} recipients).", recepients.Count()), ex); string exceptionMsg = ex.Message; log.ErrorMessages.Add(exceptionMsg); // Update work item foreach (JobWorkItem workItem in workItems) { workItem.Status = JobWorkStatus.Failed; if (exceptionMsg.Length >= 2000) { exceptionMsg = exceptionMsg.Substring(0, 1999); } workItem.Info = exceptionMsg; if (workItem.JobId > 0) { workItem.Save(); } } // can't continue break; } } while (true); // Finished log.SendStop = DateTime.Now; _log.Debug("Ending Send"); // return report return(log); }
/// <summary> /// Send mail to mailreceivers using System.Web.Mail /// </summary> /// <param name="mailInfo"></param> /// <param name="recipients"></param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A SendMailLog object with information about the status of the job.</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recipients, bool testMode) { _log.Debug("900.10.11.1 Starting Send"); // for logging SendMailLog log = new SendMailLog(); log.StartJob(); // Recipients util RecipientsUtility recipUtil = new RecipientsUtility(); // We try to verify as many settings, variables etc. before // starting the sending loop, as we don't want to generate // lots of exceptions in a loop. // Need someone to send to if (recipients.Items.Count == 0) { _log.Error("900.10.11.2 Trying to send mail with an empty JobWorkItems collection. Please check the collection before attemting to send mail."); throw new ArgumentNullException("Recipient collection is empty, there is no recipients to send to.", "recepients"); } // And, we need a sender address if (mailInfo.From == null || mailInfo.From == string.Empty) { _log.Error("900.10.11.3 Missing from address. SMTP servers do not allow sending without a sender."); throw new ArgumentNullException("Missing from address. SMTP servers do not allow sending without a sender.", "mailInfo.From"); } // Load the license. This needs to be in the EmailMessage.LoadLicenseString(GetLicenseFileContents()); // We'll reuse the mail object, so we create it outside of the loop EmailMessage mail = CreateMessage(mailInfo); // Set port and authentication details if found InitializeMailSettings(ref mail); // Send a warm-up message outside the loop. This // will help us catch any misconfigurations and other // things that prevents us from sending emails. By // doing this outside the loop, we won't kill the // application or log with uneccesary error messages. // TODO: Implement this // Loop through receivers collection, send email for each foreach (JobWorkItem workItem in recipients) { _log.Debug(string.Format("900.10.11.5 Job {0}, Email: {1}, Status: {2}", workItem.JobId.ToString(), workItem.EmailAddress, workItem.Status.ToString())); // Only attempt sending those that have been stamped as ready for sending // unless we're in a test case. if (workItem.Status == JobWorkStatus.Sending || testMode == true) { try { // At this point we assume the address is formatted correctly // and checked, but aspNetEmail checks the to address on set, may fail mail.To = workItem.EmailAddress; // Perform actual send, if testmail, then this will // return false. We should not update the status on // test sends bool result = SendMail(mail, testMode); if (result == true) { log.SuccessMessages.Add(workItem.EmailAddress); // Update status and save it // TODO: Improve performance by doing this in batch workItem.Status = JobWorkStatus.Complete; // Only save if real work item (could be a test item) if (workItem.JobId > 0) { workItem.Save(); } } // else // Could not send, it has been disabled by // settings or parameters } catch (Exception ex) { _log.Error(string.Format("900.10.11.6 Error sending to email: {0}", workItem.EmailAddress), ex); string exceptionMsg = string.Format("Email: {0}\r\nException: {1}\r\n\r\n", workItem.EmailAddress, ex.Message); log.ErrorMessages.Add(exceptionMsg); // Update work item workItem.Status = JobWorkStatus.Failed; if (exceptionMsg.Length >= 2000) { exceptionMsg = exceptionMsg.Substring(0, 1999); } workItem.Info = exceptionMsg; if (workItem.JobId > 0) { workItem.Save(); } } } else { _log.Debug(string.Format("900.10.11.7 Skipping Recipient, wrong status. Job {0}, Email: {1}, Status: {2}", workItem.JobId.ToString(), workItem.EmailAddress, workItem.Status.ToString())); } } // Finished log.StopJob(); // Warn user if logging is enabled if (Configuration.NewsLetterConfiguration.ExtendedLogFile != null) { log.WarningMessages.Add("Logging has been enabled. Only use during troubleshooting."); } _log.Debug("900.10.11.8 Ending Send"); // return report return(log); }
/// <summary> /// Creates the mail info object with only meta information like subject /// sender etc. Does not add mail content to the object. /// </summary> public virtual MailInformation GetMailMetaData(PageData mailPage) { // Get content from page MailInformation mailInfo = new MailInformation(); mailInfo.PageLink = mailPage.PageLink; mailInfo.Subject = GetMailSubject(mailPage); if (mailPage["BaseUrl"] != null) mailInfo.BaseUrl = mailPage["BaseUrl"].ToString(); else mailInfo.BaseUrl = GetSiteUrl(mailPage); // The pagename can be used for other things later on mailInfo.PageName = mailInfo.PageName; // Sender address mailInfo.From = GetMailSender(mailPage); // Campaign data mailInfo.Utm.Campaign = GetCampaign(mailPage); return mailInfo; }
/// <summary> /// /// </summary> /// <param name="mailInfo"></param> /// <param name="recepients">receivers</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A html formatted report of the send process</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode) { _log.Debug("Starting Send"); // for logging SendMailLog log = new SendMailLog(); // Need someone to send to if (recepients.Items.Count == 0) { _log.Error("Trying to send newsletter with an empty JobWorkCollection. Please check the collection before attemting to send mail."); throw new ArgumentNullException("recepients", "Recipient collection is empty, there is no recipients to send to."); } // And, we need a sender address if (string.IsNullOrEmpty(mailInfo.From)) { _log.Error("Missing from address."); throw new ArgumentNullException("mailInfo", "Missing from address."); } // Inline all css PreMailer.Net.PreMailer preMailer = new PreMailer.Net.PreMailer(mailInfo.BodyHtml); if (mailInfo.Utm.HasValidUtmCode) { preMailer.AddAnalyticsTags(mailInfo.Utm.Source, mailInfo.Utm.Medium, mailInfo.Utm.Campaign, mailInfo.Utm.Content); } InlineResult cssInline = preMailer.MoveCssInline(); mailInfo.BodyHtml = cssInline.Html; // Log any messages, debug is only detected // if we have an HttpContext. if (IsInDebugMode()) { log.WarningMessages.Add("Premailer CSS warning messages are only shown in debug mode. Primarily for developers."); log.WarningMessages.AddRange(cssInline.Warnings.ToArray()); } // Loop through receivers collection, add to collection and send // one email per batch size. int batchRun = 0; int batchSize = GetBatchSize(); do { IEnumerable<JobWorkItem> workItems = recepients.Skip(batchRun*batchSize).Take(batchSize); int numberofItemsToSend = workItems.Count(); if (numberofItemsToSend == 0) break; batchRun++; try { if (SendMailBatch(mailInfo, workItems, testMode)) { // Mark each item as sent foreach (JobWorkItem workItem in workItems) { log.SuccessMessages.Add(workItem.EmailAddress); // Update status and save it workItem.Status = JobWorkStatus.Complete; // Only save if real work item (could be a test item) if (workItem.JobId > 0) workItem.Save(); } } } catch (Exception ex) { _log.Error(string.Format("Error sending batch (to {0} recipients).", recepients.Count()), ex); string exceptionMsg = ex.Message; log.ErrorMessages.Add(exceptionMsg); // Update work item foreach (JobWorkItem workItem in workItems) { workItem.Status = JobWorkStatus.Failed; if (exceptionMsg.Length >= 2000) exceptionMsg = exceptionMsg.Substring(0, 1999); workItem.Info = exceptionMsg; if (workItem.JobId > 0) workItem.Save(); } // can't continue break; } } while (true); // Finished log.SendStop = DateTime.Now; _log.Debug("Ending Send"); // return report return log; }
/// <summary> /// Creates the aspNET EmailMessage object based on mail information /// we've alread got. Construct the message from html, and then spesify /// that the images should be attached inline. /// </summary> /// <param name="mailInfo">Mail info.</param> /// <returns></returns> private EmailMessage CreateMessage(MailInformation mailInfo) { if (_log.IsDebugEnabled()) { _log.Debug("Begin CreateMessage. Base Url: {0}", mailInfo.BaseUrl); } EmailMessage email = new EmailMessage(); HtmlUtility utility = new HtmlUtility(email); // Swallow exceptions if configured if (Configuration.NewsLetterConfiguration.DisableExceptionsInMailRendering == true) { email.ThrowException = false; } // Troubleshooting needs logging if (Configuration.NewsLetterConfiguration.ExtendedLogFile != null) { // Be careful with logging, it will generate large amounts of log data email.LogInMemory = false; email.LogPath = EPiServer.Global.BaseDirectory + Configuration.NewsLetterConfiguration.ExtendedLogFile; email.LogDebugStatements = true; email.LogBody = true; email.Logging = true; } // set all relative links contained in the html to this url string baseUrl = SiteDefinition.Current.SiteUrl.ToString(); if (string.IsNullOrEmpty(mailInfo.BaseUrl) == false) { baseUrl = mailInfo.BaseUrl; } // Resolves Hrefs to their absolute value, this means // no urls in the html will be relative (which can be a pain // depending on the markup utility.ResolveHrefs = true; // Clean html, remove tags and content we do not want or need utility.HtmlRemovalOptions = HtmlRemovalOptions.AppletTag | HtmlRemovalOptions.EmbedTag | HtmlRemovalOptions.NoScriptTag | HtmlRemovalOptions.ObjectTag | HtmlRemovalOptions.ParamTag | HtmlRemovalOptions.Scripts | HtmlRemovalOptions.ViewState | HtmlRemovalOptions.EventArgument | HtmlRemovalOptions.EventTarget; // Load the html mail utility.LoadString(mailInfo.BodyHtml, baseUrl); // Set the UrlContent base utility.SetUrlContentBase = false; // Set the basetag in the html utility.SetHtmlBaseTag = true; // Embed the images into the email message, using the // content id as a src reference. Change this if you do // not want images to be part of the message utility.EmbedImageOption = EmbedImageOption.ContentId; // If you have problems loading images, disable exceptions // if (utility.ParentMessage != null) // utility.ParentMessage.ThrowException = false; //render the Html so it is properly formatted for email utility.Render(); //render an EmailMessage with appropriate text and html parts email = utility.ToEmailMessage(); //load remaining properties from web.config email.LoadFromConfig(); // Try to get these encodings correct email.ContentTransferEncoding = MailEncoding.QuotedPrintable; // Using utf-8 would have been nice, but utf-8 is not widely // adopted by email clients it seems. // email.CharSet = "utf-8"; // Using iso 8859-1 hotmail will show Norwegian characters // even if the user runs with english settings // Hotmail needs this email.CharSet = "iso-8859-1"; // Override with our own values email.Subject = mailInfo.Subject; email.From = mailInfo.From; email.TextBodyPart = mailInfo.BodyText; if (_log.IsDebugEnabled()) { _log.Debug("Rendered Mail Message: {0}", email.Subject); } return(email); }
/// <summary> /// Send mail to mailreceivers using System.Web.Mail /// </summary> /// <param name="mailInfo">A MailInformation object with the content to send</param> /// <param name="recepients">receivers</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A report of the send process</returns> public abstract SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode);
public abstract bool SendMailBatch(MailInformation mail, IEnumerable<JobWorkItem> recipients, bool onlyTestDontSendMail);
/// <summary> /// Send mail to mailreceivers using Mailgun REST API /// </summary> /// <param name="mailInfo"></param> /// <param name="recepients">receivers</param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A html formatted report of the send process</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recepients, bool testMode) { _log.Debug("Starting Mailgun Send"); // Inline all css InlineResult cssInline = PreMailer.Net.PreMailer.MoveCssInline(mailInfo.BodyHtml); mailInfo.BodyHtml = cssInline.Html; // Base will send SendMailLog log = base.SendEmail(mailInfo, recepients, testMode); // Log any messages, debug is only detected // if we have an HttpContext. if (IsInDebugMode()) { log.WarningMessages.Add("Premailer CSS warning messages are only shown in debug mode. Primarily for developers."); log.WarningMessages.AddRange(cssInline.Warnings.ToArray()); } _log.Debug("Ending Mailgun Send"); // return report return log; }
public MailInformation GetHtmlReport(string from, string subject, bool ShowSuccessAddresses) { if (from == null) throw new ArgumentNullException("from", "From email address cannot be null"); MailInformation mailInfo = new MailInformation(); mailInfo.Subject = subject; mailInfo.From = from; System.Text.StringBuilder reportMailBody = new System.Text.StringBuilder(); reportMailBody.AppendLine("<div id=\"MailReport\">"); // Start send reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">Started send:</span> <span class=\"onelinesectionvalue\">{0}</span></span></div>", this.SendStart.ToString()); // Start send reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">End send:</span> <span class=\"onelinesectionvalue\">{0}</span></div>", this.SendStop.ToString()); // Duration TimeSpan duration = TimeSpan.FromMilliseconds((double)this.TotalDuration); reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">Duration:</span> <span class=\"onelinesectionvalue\">{0}hr {1}min {2}sec</span></div>", duration.Hours.ToString(), duration.Minutes.ToString(), duration.Seconds.ToString()); // Number of emails sent reportMailBody.AppendFormat("<div class=\"onelinesection\"><span class=\"onelinesectionlabel\">Number of emails sent:</span> <span class=\"onelinesectionvalue\">{0}</span></div>", this.NumberOfEmailsSent.ToString()); // Exceptions first if (ErrorMessages.Count > 0) AppendMultiLineSectionAsHtml("Exceptions", ErrorMessages, reportMailBody); // Warnings next if (WarningMessages.Count > 0) AppendMultiLineSectionAsHtml("Warnings", WarningMessages, reportMailBody); // Successfull HTML Text Recipients if (ShowSuccessAddresses == true) AppendMultiLineSectionAsHtml("Successfull HTML Format Recipients", SuccessMessages, reportMailBody); reportMailBody.AppendLine("</div>"); mailInfo.BodyHtml = reportMailBody.ToString(); return mailInfo; }
public override bool SendMailBatch(MailInformation mail, IEnumerable <JobWorkItem> recipients, bool onlyTestDontSendMail) { MailgunSettings settings = GetSettings(); // ReSharper disable PossibleMultipleEnumeration if (recipients == null || recipients.Count() == 0) { throw new ArgumentException("No workitems", "recipients"); } if (recipients.Count() > 1000) { throw new ArgumentOutOfRangeException("recipients", "Mailgun supports maximum 1000 recipients per batch send."); } RestClient client = new RestClient(); client.BaseUrl = new Uri("https://api.mailgun.net/v2"); client.Authenticator = new HttpBasicAuthenticator("api", settings.ApiKey); if (string.IsNullOrEmpty(settings.ProxyAddress) == false) { client.Proxy = new WebProxy(settings.ProxyAddress, settings.ProxyPort); // Makes it easy to debug as Fiddler will show us the requests } RestRequest request = new RestRequest(); request.AlwaysMultipartFormData = true; request.AddParameter("domain", settings.Domain, ParameterType.UrlSegment); request.Resource = "{domain}/messages"; request.AddParameter("from", mail.From); request.AddParameter("subject", mail.Subject); request.AddParameter("text", mail.BodyText); request.AddParameter("html", mail.BodyHtml); if (mail.EnableTracking) { request.AddParameter("o:tracking", mail.EnableTracking); request.AddParameter("o:tracking-clicks", mail.EnableTracking); request.AddParameter("o:tracking-opens", mail.EnableTracking); } foreach (KeyValuePair <string, object> customProperty in mail.CustomProperties) { request.AddParameter(customProperty.Key, customProperty.Value.ToString()); } // Add custom data about job if (mail.CustomProperties.ContainsKey("v:newsletter-data") == false) { request.AddParameter("v:newsletter-data", string.Format("{{\"id\": \"{0}\", \"page\": \"{1}\" }}", recipients.First().JobId, mail.PageLink.ID)); } // Add all recipients StringBuilder recipVariables = new StringBuilder(); bool first = true; foreach (JobWorkItem recipient in recipients) { request.AddParameter("to", recipient.EmailAddress); if (first == false) { recipVariables.Append(","); } first = false; recipVariables.AppendFormat("\"{0}\" : {{\"id\": \"{1}\" }}", recipient.EmailAddress, recipient.GetHashCode()); } request.AddParameter("recipient-variables", "{" + recipVariables.ToString() + "}"); //if(onlyTestDontSendMail) //{ // request.AddParameter("o:testmode", true); //} request.Method = Method.POST; var response = client.Execute(request); Dictionary <string, string> resultParams = null; try { resultParams = new JsonDeserializer().Deserialize <Dictionary <string, string> >(response); } catch (Exception e) { _log.Warning("Unable to parse Mailgun response.", e); } if (response.StatusCode == HttpStatusCode.OK) { _log.Debug("Mailgun responded with: {0} - {1}", response.StatusCode, response.StatusDescription); if (string.IsNullOrEmpty(response.ErrorMessage) == false) { _log.Error("Response Error: {0}", response.ErrorMessage); } _log.Debug(response.Content); // json looks like: //{ // "message": "Queued. Thank you.", // "id": "<*****@*****.**>" //} // Update all recipients with information if (resultParams != null) { string info = resultParams["id"]; foreach (JobWorkItem recipient in recipients) { recipient.Info = info; } } } else { _log.Debug("Mailgun responded with: {0} - {1}", response.StatusCode, response.StatusDescription); string errorMessage = response.StatusDescription; if (resultParams != null) { errorMessage = resultParams["message"]; } if (string.IsNullOrEmpty(response.ErrorMessage) == false) { _log.Error("Response Error: {0}", response.ErrorMessage); } _log.Debug(response.Content); throw new HttpException((int)response.StatusCode, errorMessage); } return(true); // ReSharper restore PossibleMultipleEnumeration }
/// <summary> /// Send mail to mailreceivers using System.Web.Mail /// </summary> /// <param name="mailInfo"></param> /// <param name="recipients"></param> /// <param name="testMode">No mails are sent, generating report for user</param> /// <returns>A SendMailLog object with information about the status of the job.</returns> public override SendMailLog SendEmail(MailInformation mailInfo, JobWorkItems recipients, bool testMode) { _log.Debug("900.10.11.1 Starting Send"); // for logging SendMailLog log = new SendMailLog(); log.StartJob(); // Recipients util RecipientsUtility recipUtil = new RecipientsUtility(); // We try to verify as many settings, variables etc. before // starting the sending loop, as we don't want to generate // lots of exceptions in a loop. // Need someone to send to if (recipients.Items.Count == 0) { _log.Error("900.10.11.2 Trying to send mail with an empty JobWorkItems collection. Please check the collection before attemting to send mail."); throw new ArgumentNullException("Recipient collection is empty, there is no recipients to send to.", "recepients"); } // And, we need a sender address if (mailInfo.From == null || mailInfo.From == string.Empty) { _log.Error("900.10.11.3 Missing from address. SMTP servers do not allow sending without a sender."); throw new ArgumentNullException("Missing from address. SMTP servers do not allow sending without a sender.", "mailInfo.From"); } // Load the license. This needs to be in the EmailMessage.LoadLicenseString(GetLicenseFileContents()); // We'll reuse the mail object, so we create it outside of the loop EmailMessage mail = CreateMessage(mailInfo); // Set port and authentication details if found InitializeMailSettings(ref mail); // Send a warm-up message outside the loop. This // will help us catch any misconfigurations and other // things that prevents us from sending emails. By // doing this outside the loop, we won't kill the // application or log with uneccesary error messages. // TODO: Implement this // Loop through receivers collection, send email for each foreach (JobWorkItem workItem in recipients) { _log.Debug(string.Format("900.10.11.5 Job {0}, Email: {1}, Status: {2}", workItem.JobId.ToString(), workItem.EmailAddress, workItem.Status.ToString())); // Only attempt sending those that have been stamped as ready for sending // unless we're in a test case. if (workItem.Status == JobWorkStatus.Sending || testMode == true) { try { // At this point we assume the address is formatted correctly // and checked, but aspNetEmail checks the to address on set, may fail mail.To = workItem.EmailAddress; // Perform actual send, if testmail, then this will // return false. We should not update the status on // test sends bool result = SendMail(mail, testMode); if (result == true) { log.SuccessMessages.Add(workItem.EmailAddress); // Update status and save it // TODO: Improve performance by doing this in batch workItem.Status = JobWorkStatus.Complete; // Only save if real work item (could be a test item) if (workItem.JobId > 0) workItem.Save(); } // else // Could not send, it has been disabled by // settings or parameters } catch (Exception ex) { _log.Error(string.Format("900.10.11.6 Error sending to email: {0}", workItem.EmailAddress), ex); string exceptionMsg = string.Format("Email: {0}\r\nException: {1}\r\n\r\n", workItem.EmailAddress, ex.Message); log.ErrorMessages.Add(exceptionMsg); // Update work item workItem.Status = JobWorkStatus.Failed; if (exceptionMsg.Length >= 2000) exceptionMsg = exceptionMsg.Substring(0, 1999); workItem.Info = exceptionMsg; if (workItem.JobId > 0) workItem.Save(); } } else { _log.Debug(string.Format("900.10.11.7 Skipping Recipient, wrong status. Job {0}, Email: {1}, Status: {2}", workItem.JobId.ToString(), workItem.EmailAddress, workItem.Status.ToString())); } } // Finished log.StopJob(); // Warn user if logging is enabled if (Configuration.NewsLetterConfiguration.ExtendedLogFile != null) log.WarningMessages.Add("Logging has been enabled. Only use during troubleshooting."); _log.Debug("900.10.11.8 Ending Send"); // return report return log; }
/// <summary> /// Creates the aspNET EmailMessage object based on mail information /// we've alread got. Construct the message from html, and then spesify /// that the images should be attached inline. /// </summary> /// <param name="mailInfo">Mail info.</param> /// <returns></returns> private EmailMessage CreateMessage(MailInformation mailInfo) { if (_log.IsDebugEnabled()) _log.Debug("Begin CreateMessage. Base Url: {0}", mailInfo.BaseUrl); EmailMessage email = new EmailMessage(); HtmlUtility utility = new HtmlUtility(email); // Swallow exceptions if configured if (Configuration.NewsLetterConfiguration.DisableExceptionsInMailRendering == true) email.ThrowException = false; // Troubleshooting needs logging if (Configuration.NewsLetterConfiguration.ExtendedLogFile != null) { // Be careful with logging, it will generate large amounts of log data email.LogInMemory = false; email.LogPath = EPiServer.Global.BaseDirectory + Configuration.NewsLetterConfiguration.ExtendedLogFile; email.LogDebugStatements = true; email.LogBody = true; email.Logging = true; } // set all relative links contained in the html to this url string baseUrl = SiteDefinition.Current.SiteUrl.ToString(); if (string.IsNullOrEmpty(mailInfo.BaseUrl) == false) baseUrl = mailInfo.BaseUrl; // Resolves Hrefs to their absolute value, this means // no urls in the html will be relative (which can be a pain // depending on the markup utility.ResolveHrefs = true; // Clean html, remove tags and content we do not want or need utility.HtmlRemovalOptions = HtmlRemovalOptions.AppletTag | HtmlRemovalOptions.EmbedTag | HtmlRemovalOptions.NoScriptTag | HtmlRemovalOptions.ObjectTag | HtmlRemovalOptions.ParamTag | HtmlRemovalOptions.Scripts | HtmlRemovalOptions.ViewState | HtmlRemovalOptions.EventArgument | HtmlRemovalOptions.EventTarget; // Load the html mail utility.LoadString(mailInfo.BodyHtml, baseUrl); // Set the UrlContent base utility.SetUrlContentBase = false; // Set the basetag in the html utility.SetHtmlBaseTag = true; // Embed the images into the email message, using the // content id as a src reference. Change this if you do // not want images to be part of the message utility.EmbedImageOption = EmbedImageOption.ContentId; // If you have problems loading images, disable exceptions // if (utility.ParentMessage != null) // utility.ParentMessage.ThrowException = false; //render the Html so it is properly formatted for email utility.Render(); //render an EmailMessage with appropriate text and html parts email = utility.ToEmailMessage(); //load remaining properties from web.config email.LoadFromConfig(); // Try to get these encodings correct email.ContentTransferEncoding = MailEncoding.QuotedPrintable; // Using utf-8 would have been nice, but utf-8 is not widely // adopted by email clients it seems. // email.CharSet = "utf-8"; // Using iso 8859-1 hotmail will show Norwegian characters // even if the user runs with english settings // Hotmail needs this email.CharSet = "iso-8859-1"; // Override with our own values email.Subject = mailInfo.Subject; email.From = mailInfo.From; email.TextBodyPart = mailInfo.BodyText; if (_log.IsDebugEnabled()) _log.Debug("Rendered Mail Message: {0}", email.Subject); return email; }