protected void ButtonPreview_Click(object sender, EventArgs e) { Person currentPerson = Person.FromIdentity(Convert.ToInt32(HttpContext.Current.User.Identity.Name)); Organization org = Organization.FromIdentity(Convert.ToInt32(this.DropOrganizations.SelectedValue)); Geography geo = Geography.FromIdentity(Convert.ToInt32(this.DropGeographies.SelectedValue)); TypedMailTemplate template; if (this.DropRecipients.SelectedValue == "Members") { MemberMail membermail = new MemberMail(); template = membermail; membermail.pSubject = this.TextSubject.Text; membermail.pBodyContent = this.RadEditorBody.Content; membermail.pOrgName = org.MailPrefixInherited; membermail.pGeographyName = (geo.Identity == Geography.RootIdentity ? org.NameShort : geo.Name); } else { OfficerMail officermail = new OfficerMail(); template = officermail; officermail.pSubject = this.TextSubject.Text; officermail.pBodyContent = this.RadEditorBody.Content; officermail.pOrgName = org.MailPrefixInherited; officermail.pGeographyName = (geo.Identity == Geography.RootIdentity ? org.NameShort : geo.Name); } OutboundMail fake = template.CreateOutboundFake(currentPerson, org, geo); this.LiteralPreview.Text = fake.RenderHtml(currentPerson, org.DefaultCountry.Culture); this.RadEditorBody.Focus(); }
private void CreatePreview() { Person currentPerson = Person.FromIdentity(Convert.ToInt32(HttpContext.Current.User.Identity.Name)); Organization org = Organization.FromIdentity(Convert.ToInt32(this.DropOrganizations.SelectedValue)); if (this.DropGeographies.SelectedIndex > -1) { Geography geo = Geography.FromIdentity(Convert.ToInt32(this.DropGeographies.SelectedValue)); WelcomeMail welcomemail = new WelcomeMail(); welcomemail.pSubject = this.TextSubject.Text; welcomemail.pBodyContent = this.TextBody.Text; welcomemail.pOrgName = org.MailPrefixInherited; welcomemail.pGeographyName = ""; if (geo.Identity != Geography.RootIdentity) { welcomemail.pGeographyName = geo.Name; } OutboundMail fake = welcomemail.CreateOutboundFake(currentPerson, org, geo); this.LiteralPreview.Text = fake.RenderHtml(currentPerson, org.DefaultCountry.Culture); } this.TextBody.Focus(); }
public void SendNotice(TypedMailTemplate mailtempl, int organizationId, bool asOfficer) { OutboundMail mail = mailtempl.CreateFunctionalOutboundMail( asOfficer ? MailAuthorType.PirateWeb : MailAuthorType.Service, OutboundMail.PriorityNormal, Organization.FromIdentity(organizationId), Geography.Root, DateTime.Now); mail.AddRecipient(this, asOfficer); mail.SetRecipientCount(1); mail.SetResolved(); mail.SetReadyForPickup(); }
protected void ButtonSend_Click(object sender, EventArgs e) { if (this.TextBody.Text.Length > 1) { ClientScript.RegisterStartupScript(this.GetType(), "StartupMessage", "alert('Your mail has been placed on the outbound queue. You will receive status reports in e-mail as the transmission progresses.');", true); Person currentPerson = Person.FromIdentity(Convert.ToInt32(HttpContext.Current.User.Identity.Name)); Organization org = Organization.FromIdentity(Convert.ToInt32(this.DropOrganizations.SelectedValue)); if (this.DropGeographies.SelectedIndex < 0) { return; } Geography geo = Geography.FromIdentity(Convert.ToInt32(this.DropGeographies.SelectedValue)); TypedMailTemplate template; if (this.DropRecipients.SelectedValue == "Members") { MemberMail membermail = new MemberMail(); template = membermail; membermail.pSubject = this.TextSubject.Text; membermail.pBodyContent = this.TextBody.Text; membermail.pOrgName = org.MailPrefixInherited; membermail.pGeographyName = (geo.Identity == Geography.RootIdentity ? org.NameShort : geo.Name); } else { OfficerMail officermail = new OfficerMail(); template = officermail; officermail.pSubject = this.TextSubject.Text; officermail.pBodyContent = this.TextBody.Text; officermail.pOrgName = org.MailPrefixInherited; officermail.pGeographyName = (geo.Identity == Geography.RootIdentity ? org.NameShort : geo.Name); } OutboundMail mail = template.CreateOutboundMail(currentPerson, OutboundMail.PriorityNormal, org, geo); // We're not resolving recipients here, but deferring that to PirateBot after pickup mail.SetReadyForPickup(); } else { ClientScript.RegisterStartupScript(this.GetType(), "StartupMessage", "alert('Nothing to do, the mail is empty...');", true); } }
protected void ButtonTest_Click(object sender, EventArgs e) { int actingOrgId = (int)ViewState["actingOrg"]; if (actingOrgId == 0) { ErrorMsg.Text = "No organisation!"; return; } Geography geography = this.GeographyTree.SelectedGeography; Organization actingOrg = Organization.FromIdentity(actingOrgId); if (this.CheckSms.Checked) { string smsText = this.TextSms.Text; if (!string.IsNullOrEmpty(smsText) && smsText.Trim().Length > 3) // 3 characters is too small message, should always be more { _currentUser.SendPhoneMessage("PP: " + smsText.Trim()); ChargeBudget(SelectBudget(geography), PhoneMessageTransmitter.SMSCost, "Test SMS"); } } if (this.CheckMail.Checked) { ActivistMail activistMail = new ActivistMail(); activistMail.pSubject = this.TextMailSubject.Text; activistMail.pBodyContent = this.TextMailBody.Text; activistMail.pOrgName = actingOrg.MailPrefixInherited; activistMail.pGeographyName = (geography.Identity == Geography.RootIdentity ? "" : geography.Name); OutboundMail mail = activistMail.CreateFunctionalOutboundMail(MailAuthorType.ActivistService, OutboundMail.PriorityNormal, actingOrg, geography); mail.AddRecipient(_currentUser, false); mail.SetRecipientCount(1); mail.SetResolved(); mail.SetReadyForPickup(); } PanelTestSent.Visible = true; }
public bool CreateOutboundMail(OutboundMail mail) { return(this.Post(API_FRESHDESK_OUTBOUND_EMAIL, mail.ToJson())); }
private static void CheckOneFeed(string readerUrl, string persistAsKey, int orgIdForTemplate) { string persistenceKey = String.Format("Pressrelease-Highwater-{0}", persistAsKey); DateTime highWaterMark = DateTime.MinValue; RssReader reader = null; try { string highWaterMarkString = Persistence.Key[persistenceKey]; if (string.IsNullOrEmpty(highWaterMarkString)) { //Initialize highwatermark if never used highWaterMark = DateTime.Now; Persistence.Key[persistenceKey] = DateTime.Now.ToString(); } else { try { highWaterMark = DateTime.Parse(highWaterMarkString); } catch (Exception ex) { HeartBeater.Instance.SuggestRestart(); throw new Exception( "Triggered restart. Unable to read/parse old highwater mark from database in PressReleaseChecker.Run(), from key:" + persistenceKey + ", loaded string was '" + highWaterMarkString + "' expected format is " + DateTime.Now, ex); } } DateTime storedHighWaterMark = highWaterMark; reader = new RssReader(readerUrl); Rss rss = reader.Read(); foreach (RssChannelItem item in rss.Channel.Items) { // Ignore any items older than the highwater mark. // Also ignore if older than two days if (item.PubDate < highWaterMark || item.PubDate < DateTime.Now.AddDays(-2)) { continue; } // This is an item we should publish. // Set highwater datetime mark. We do this first, BEFORE processing, as a defense against mail floods, // if should something go wrong and unexpected exceptions happen. // We used to add 70 minutes as a defense against mistakes on DST switch in spring and fall (yes, it has happened), but have reduced to two. if (item.PubDate > storedHighWaterMark) { Persistence.Key[persistenceKey] = item.PubDate.AddMinutes(2).ToString(); storedHighWaterMark = item.PubDate.AddMinutes(2); // Verify that it was written correctly to database. This is defensive programming to avoid a mail flood, // in case we can't write to the database for some reason. string newStoredHighWaterString = ""; try { newStoredHighWaterString = Persistence.Key[persistenceKey]; DateTime temp = DateTime.Parse(newStoredHighWaterString); } catch (Exception ex) { throw new Exception( "Unable to commit/parse new highwater mark to database in PressReleaseChecker.Run(), loaded string was '" + newStoredHighWaterString + "'", ex); } if (DateTime.Parse(Persistence.Key[persistenceKey]) < item.PubDate) { throw new Exception( "Unable to commit new highwater mark to database in PressReleaseChecker.Run()"); } } bool allReporters = false; bool international = false; MediaCategories categories = new MediaCategories(); foreach (RssCategory category in item.Categories) { if (category.Name == "Alla") { allReporters = true; } else if (category.Name == "Uncategorized") { } else { try { MediaCategory mediaCategory = MediaCategory.FromName(category.Name); categories.Add(mediaCategory); if (category.Name.StartsWith("International")) { international = true; } } catch (Exception) { ExceptionMail.Send( new Exception("Unrecognized media category in press release: " + category.Name)); } } } string mailText = Blog2Mail(item.Content); // Create recipient list of relevant reporters Reporters reporters = null; if (allReporters) { reporters = Reporters.GetAll(); } else { reporters = Reporters.FromMediaCategories(categories); } // Add officers if not int'l People officers = new People(); Dictionary <int, bool> officerLookup = new Dictionary <int, bool>(); if (!international) { int[] officerIds = Roles.GetAllDownwardRoles(1, 1); foreach (int officerId in officerIds) { officerLookup[officerId] = true; } } else { officerLookup[1] = true; } // Send press release //TODO: hardcoded geo ... using World Organization org = Organization.FromIdentity(orgIdForTemplate); Geography geo = Geography.Root; PressReleaseMail pressreleasemail = new PressReleaseMail(); pressreleasemail.pSubject = item.Title; pressreleasemail.pDate = DateTime.Now; pressreleasemail.pBodyContent = Blog2Mail(item.Content); pressreleasemail.pOrgName = org.MailPrefixInherited; if (allReporters) { pressreleasemail.pPostedToCategories = "Alla"; // TODO: TRANSLATE } else if (international) { pressreleasemail.pPostedToCategories = "International/English"; // TODO: THIS IS HARDCODED } else { pressreleasemail.pPostedToCategories = PressReleaseMail.GetConcatenatedCategoryString(categories); } OutboundMail newMail = pressreleasemail.CreateFunctionalOutboundMail(MailAuthorType.PressService, OutboundMail.PriorityHighest, org, geo); int recipientCount = 0; foreach (Reporter recipient in reporters) { if (!Formatting.ValidateEmailFormat(recipient.Email)) { continue; } ++recipientCount; newMail.AddRecipient(recipient); } foreach (int key in officerLookup.Keys) { Person recipient = Person.FromIdentity(key); if (!Formatting.ValidateEmailFormat(recipient.Mail)) { continue; } ++recipientCount; newMail.AddRecipient(recipient, true); } newMail.SetRecipientCount(recipientCount); newMail.SetResolved(); newMail.SetReadyForPickup(); } } catch (Exception ex) { ExceptionMail.Send( new Exception("PressReleaseChecker failed:" + ex.Message + "\r\nwhen checking " + readerUrl, ex)); } finally { reader.Close(); } }
public static string CreateWelcomeMail(Person person, Organization organization) { // for this person, iterate over all applicable geographies and organizations Organizations orgLine = organization.GetLine(); Geographies geoLine = person.Geography.GetLine(); orgLine.Reverse(); // Start at the most local org Dictionary <int, bool> orgMailedLookup = new Dictionary <int, bool>(); int delay = 0; string result = string.Empty; Random random = new Random(); foreach (Organization org in orgLine) { foreach (Geography geo in geoLine) // but at the top geography { AutoMail autoMail = AutoMail.FromTypeOrganizationAndGeography(AutoMailType.Welcome, org, geo); if (autoMail == null) { continue; } Person lead = null; string geoName = geo.Name; try { lead = Roles.GetLocalLead(org, geo); orgMailedLookup[org.Identity] = true; // Make sure that the chairman doesn't mail at a lower level } catch (ArgumentException) { } if (lead == null && !orgMailedLookup.ContainsKey(org.Identity)) { // If we get here, there is a mail template at the highest possible geo for this org, but no local lead. // That's usually the case with board-centric organizations rather than executive-centric. // Try to mail from chairman rather than the local lead. try { orgMailedLookup[org.Identity] = true; lead = Roles.GetChairman(org); geoName = "Chairman"; } catch (ArgumentException) { } } if (lead == null) { // ok, give up if there isn't a chairman either or if we've already mailed this org from the chairman continue; } // TODO: fetch lead from roles WelcomeMail welcomemail = new WelcomeMail(); welcomemail.pOrgName = org.MailPrefixInherited; welcomemail.pGeographyName = ""; if (geo.Identity != Geography.RootIdentity) { welcomemail.pGeographyName = geo.Name; } welcomemail.pBodyContent = autoMail.Body; welcomemail.pSubject = autoMail.Title; OutboundMail newMail = welcomemail.CreateOutboundMail(lead, OutboundMail.PriorityNormal, org, geo, DateTime.Now.AddMinutes(delay)); newMail.AddRecipient(person.Identity, false); newMail.SetRecipientCount(1); newMail.SetResolved(); newMail.SetReadyForPickup(); result += String.Format(" - {0}/{1} by {2} (", org.NameShort, geoName, lead.Canonical); if (delay == 0) { result += "sent now"; delay += 37; } else { result += "sending at " + DateTime.Now.AddMinutes(delay).ToString("HH:mm"); delay += 31 + random.Next(52); } result += ")\r\n"; } } if (result.Length < 4) { result = "none\r\n"; } return(result); }
public static void Run() { OutboundMail mail = OutboundMail.GetFirstUnresolved(); if (mail == null) { return; // No unresolved -- nothing to do } // Use a dictionary to add people int downwardCount = 0; int upwardCount = 0; Dictionary <int, bool> officerIds = new Dictionary <int, bool>(); Dictionary <int, bool> personIds = new Dictionary <int, bool>(); // Add the affected people switch (mail.MailType) { case (int)TypedMailTemplate.TemplateType.MemberMail: // All members at this org and geography People people = People.FromOrganizationAndGeography(mail.Organization, mail.Geography); downwardCount = people.Count; foreach (Person person in people) { personIds[person.Identity] = true; } break; case (int)TypedMailTemplate.TemplateType.OfficerMail: // All officers at this org and geography int[] officers = Roles.GetAllDownwardRoles(mail.OrganizationId, mail.GeographyId); downwardCount = officers.Length; foreach (int personId in officers) { officerIds[personId] = true; } break; default: throw new InvalidOperationException("Unhandled mail mode; can't resolve mail id " + mail.Identity); } int[] upwardIds = Roles.GetAllUpwardRoles(mail.OrganizationId, mail.GeographyId); upwardCount = upwardIds.Length; foreach (int personId in upwardIds) { // Filter for subscription of copies to local mail. Inefficient filter, room for optimization. if (Person.FromIdentity(personId).IsSubscribing(NewsletterFeed.TypeID.OfficerUpwardCopies)) { officerIds[personId] = true; } } // Assemble arrays List <int> officerIdList = new List <int>(); foreach (int officerId in officerIds.Keys) { officerIdList.Add(officerId); if (personIds.ContainsKey(officerId)) { // If somebody is going to recieve this mail both due to area coverage and // chain of command, send as chain of command only personIds.Remove(officerId); } } List <int> personIdList = new List <int>(); foreach (int personId in personIds.Keys) { personIdList.Add(personId); } // Check for people who have declined local mail Dictionary <int, bool> decliners = new Dictionary <int, bool>(); int declineCount = decliners.Count; if (declineCount > 0) { foreach (int personId in decliners.Keys) { personIds.Remove(personId); } // Rebuild list after decliners gone personIdList = new List <int>(); foreach (int personId in personIds.Keys) { personIdList.Add(personId); } } // TODO: Get queue count to give a time estimate until transmission // Create recipients mail.AddRecipients(personIdList.ToArray(), false); mail.AddRecipients(officerIdList.ToArray(), true); int countTotal = personIdList.Count + officerIdList.Count; // Mark as ready mail.SetRecipientCount(countTotal); mail.SetResolved(); // TODO: Set recipient count of mail // Create a mail to the author string mailBody = "Your outbound mail with the title \"" + mail.Title + "\" has been resolved for recipients and " + "will be sent to " + countTotal.ToString("#,##0") + " people.\r\n\r\nOut of these " + countTotal.ToString("#,##0") + ", " + (downwardCount - declineCount).ToString("#,##0") + " are people within the requested " + "organization/geography (" + Organization.FromIdentity(mail.OrganizationId).Name + "/" + Geography.FromIdentity(mail.GeographyId).Name + "), and " + upwardCount.ToString("#,##0") + " are people in the chain of command who are " + "copied on the mail to know what's happening in the organization. There is normally some overlap between these two groups.\r\n\r\n"; if (declineCount > 1) { mailBody += declineCount.ToString("#,##0") + " people will not receive the message because they have declined local mail.\r\n\r\n"; } else if (declineCount == 1) { mailBody += "One person will not receive the message because he or she has declined local mail.\r\n\r\n"; } mailBody += "Transmissions will begin " + (mail.ReleaseDateTime < DateTime.Now ? "immediately" : "in " + (mail.ReleaseDateTime - DateTime.Now).Minutes + " minutes") + ".\r\n"; new MailTransmitter( "PirateWeb", "*****@*****.**", "Mail resolved: " + mail.Title + " (" + countTotal + " recipients)", mailBody, Person.FromIdentity(mail.AuthorPersonId), true).Send(); }
internal static OutboundMailRecipient TransmitOneMail(OutboundMailRecipient recipient) { try { // If the mail address in illegal format, do not try to send anything: if (!Formatting.ValidateEmailFormat(recipient.EmailPerson.Email.Trim())) { string msg = "Invalid email address:\r\nEmailPerson [" + recipient.EmailPerson.Identity + "], mail [" + recipient.EmailPerson.Email + "]\r\nwill not send mail:" + recipient.OutboundMail.Title; throw new InvalidRecipientException(msg, null); } // If the mail address is marked as unreachable, do not try to send anything if (recipient.Person != null && recipient.Person.MailUnreachable) { string msg = "MailUnreachable email address:\r\nEmailPerson [" + recipient.EmailPerson.Identity + "], mail [" + recipient.EmailPerson.Email + "]\r\nwill not send mail:" + recipient.OutboundMail.Title; throw new InvalidRecipientException(msg, null); } // If the mail address is marked as unreachable, do not try to send anything if (recipient.Person != null && recipient.Person.NeverMail) { string msg = "NeverMail email address:\r\nEmailPerson [" + recipient.EmailPerson.Identity + "], mail [" + recipient.EmailPerson.Email + "]\r\nwill not send mail:" + recipient.OutboundMail.Title; throw new IgnoreRecipientException(msg, null); } // Otherwise, let's start processing OutboundMail mail = recipient.OutboundMail; bool limitToLatin1 = false; bool limitToText = false; Encoding currentEncoding = Encoding.UTF8; string email = recipient.EmailPerson.Email.ToLower(); if (mail.MailType == 0 || mail.TemplateName.EndsWith("Plain")) { limitToText = true; } // TEST: Does this user require the use of a text-only message (as opposed to multipart/alternative)? if (recipient.Person != null && recipient.Person.LimitMailToText) { limitToText = true; } // This is supposedly not a problem anymore //if (email.EndsWith("@hotmail.com") || email.EndsWith("@msn.com")) //{ // limitToLatin1 = true; //} // TEST: Does this user require the limited use of the Latin-1 charset (as opposed to Unicode?) if (recipient.Person != null && recipient.Person.LimitMailToLatin1) { limitToLatin1 = true; } // Capability tests end here if (limitToLatin1) { currentEncoding = Encoding.GetEncoding("ISO-8859-1"); } else { currentEncoding = Encoding.UTF8; } QuotedPrintable qp = QuotedPrintableEncoder[currentEncoding]; MailMessage message = new MailMessage(); if (mail.AuthorType == MailAuthorType.Person) { try { message.From = new MailAddress(mail.Author.PartyEmail, qp.EncodeMailHeaderString(mail.Author.Name + " (" + mail.Organization.MailPrefixInherited + ")"), currentEncoding); if (mail.Author.Identity == 1) { //TODO: Create alternative party mail optional data field, or organization chairman (based on roles) differently // Ugly hack message.From = new MailAddress("*****@*****.**", qp.EncodeMailHeaderString(mail.Author.Name + " (" + mail.Organization.MailPrefixInherited + ")"), currentEncoding); } } catch (Exception ex) { throw new InvalidSenderException( "Invalid author address in MailProcessor.TransmitOneMail:" + (mail.AuthorPersonId) + ";" + mail.Author.PartyEmail, ex); } } else { try { FunctionalMail.AddressItem aItem = mail.Organization.GetFunctionalMailAddressInh(mail.AuthorType); message.From = new MailAddress(aItem.Email, qp.EncodeMailHeaderString(aItem.Name), currentEncoding); } catch (Exception ex) { throw new InvalidSenderException( "Unknown MailAuthorType in MailProcessor.TransmitOneMail:" + ((int)mail.AuthorType), ex); } } if (recipient.AsOfficer && recipient.Person != null) { try { message.To.Add(new MailAddress(recipient.Person.PartyEmail, qp.EncodeMailHeaderString(recipient.Person.Name + " (" + mail.Organization.MailPrefixInherited + ")"), currentEncoding)); } catch (FormatException e) { string msg = "Invalid officer email address:\r\nperson [" + recipient.Person.Identity + "], mail [" + recipient.Person.PartyEmail + "]\r\nwill not send mail:" + recipient.OutboundMail.Title; throw new InvalidRecipientException(msg, e); } } else { try { message.To.Add(new MailAddress(recipient.EmailPerson.Email, qp.EncodeMailHeaderString(recipient.EmailPerson.Name), currentEncoding)); } catch (FormatException e) { string msg = "Invalid email address:\r\nEmailPerson [" + recipient.EmailPerson.Identity + "], mail [" + recipient.EmailPerson.Email + "]\r\nwill not send mail:" + recipient.OutboundMail.Title; throw new InvalidRecipientException(msg, e); } } string culture = mail.Organization.DefaultCountry.Culture; // UGLY UGLY UGLY HACK, NEEDS TO CHANGE ASAP: // We need to determine the culture of the recipient in order to use the right template. However, this is also dependent on the text body, which needs to be // in the same culture. At this point, we don't have the mail/recipient cultures in the schema. This would be the correct solution. // The INCORRECT but working solution is to do as we do here and check if a) it's a reporter and b) the reporter has International/English as a category. If so, // we change the culture to en-US. It's an ugly as all hell hack but it should work as a temporary stopgap. if (recipient.Reporter != null) { MediaCategories categories = recipient.Reporter.MediaCategories; foreach (MediaCategory category in categories) { if (category.Name == "International/English") { culture = Strings.InternationalCultureCode; break; } } } if (limitToText) { // if just text, then just add a plaintext body; string text = ""; //Cant really see any reson the HtmlAgilityPack shouldn't be thread safe, but what the heck, just in case.. lock (lockObject) { try { text = mail.RenderText(recipient.EmailPerson, culture); } catch (Exception ex) { throw new RemoveRecipientException( "TextRendering failed for " + mail.Title + " to " + recipient.EmailPerson.Email + " will not retry.\n", ex); } } message.BodyEncoding = currentEncoding; message.Body = text; } else { // otherwise, add a multipart/alternative with text and HTML string text = ""; string html = ""; //Cant really see any reson the HtmlAgilityPack shouldn't be thread safe, but what the heck, just in case.. Exception ex = null; lock (lockObject) { try { text = mail.RenderText(recipient.EmailPerson, culture); html = mail.RenderHtml(recipient.EmailPerson, culture); } catch (Exception e) { ex = e; } } if (text == "") { throw new RemoveRecipientException( "Rendering (text) failed for " + mail.Title + " to " + recipient.EmailPerson.Email + " will not retry.\n", ex); } if (html == "" || ex != null) { throw new RemoveRecipientException( "Rendering (html) failed for " + mail.Title + " to " + recipient.EmailPerson.Email + " will not retry.\n", ex); } ContentType textContentType = new ContentType(MediaTypeNames.Text.Plain); textContentType.CharSet = currentEncoding.BodyName; ContentType htmlContentType = new ContentType(MediaTypeNames.Text.Html); htmlContentType.CharSet = currentEncoding.BodyName; AlternateView textView = null; AlternateView htmlView = null; if (limitToLatin1) { textView = new AlternateView(new MemoryStream(currentEncoding.GetBytes(text)), textContentType); htmlView = new AlternateView(new MemoryStream(currentEncoding.GetBytes(text)), htmlContentType); } else { textView = AlternateView.CreateAlternateViewFromString(text, textContentType); htmlView = AlternateView.CreateAlternateViewFromString(html, htmlContentType); } // A f*****g stupid Mono bug forces us to transfer-encode in base64: it can't encode qp properly // (the "=" is not encoded to "=3D") htmlView.TransferEncoding = TransferEncoding.Base64; textView.TransferEncoding = TransferEncoding.Base64; // Add the views in increasing order of preference message.AlternateViews.Add(textView); message.AlternateViews.Add(htmlView); } if (mail.AuthorType == MailAuthorType.PirateWeb) { message.Subject = mail.Title; } else if (mail.MailType == 0) { message.Subject = mail.Organization.MailPrefixInherited + ": " + mail.Title; } else { //Title is set up in template processing in OutboundMail rendering. message.Subject = mail.Title; } message.SubjectEncoding = currentEncoding; string smtpServer = ConfigurationManager.AppSettings["SmtpServer"]; if (Debugger.IsAttached) { Debug.WriteLine("sending " + message.Subject + " to " + recipient.EmailPerson.Email); Thread.Sleep(200); //simulate delay } if (smtpServer.ToLower() != "none") { if (smtpServer == null || smtpServer.Length < 2) { smtpServer = "localhost"; } try { SmtpClient client = new SmtpClient(smtpServer, 25); client.Send(message); } catch (SmtpException e) { if (e.ToString().StartsWith("System.Net.Mail.SmtpException: 4")) { // Temporary error (SMTP 4xx). Try again. Thread.Sleep(2000); // Allow 2 seconds pause to wait for smtp-server to become available throw new ReportAndRetryRecipientException("Temporary smtp error, will retry.", e); } // Otherwise, bad recipient (assume so). Have the mail removed from the queue. List <string> recipients = new List <string>(); foreach (MailAddress address in message.To) { recipients.Add(address.Address); } ExceptionMail.Send( new ArgumentException( "Bad Recipients when sending to " + recipient.EmailPerson.Email + ": " + String.Join(", ", recipients.ToArray()), e)); if (mail.AuthorType == MailAuthorType.Person) { try { mail.Author.SendOfficerNotice( "Failed recipient(s): " + String.Join(", ", recipients.ToArray()), "Some recipients failed inexplicably in a mail from you.", 1); } catch (Exception ex) { throw new Exception("Failed to SendOfficerNotice to :" + mail.AuthorPersonId, ex); } } } } return(recipient); // To pass this object onto the we're-done callback } catch (InvalidRecipientException ex) { throw ex; } catch (RetryRecipientException ex) { Thread.Sleep(2000); // Allow 2 seconds pause to avoid flooding the errorlog too fast in case of a permanent failure throw ex; } catch (Exception ex) { throw ex; } }
public static void SendReminderMail(Person person, Memberships memberships) { // First, determine the organization template to use. Prioritize a long ancestry. // This is a hack for the Swedish structure. ReminderMail remindermail = new ReminderMail(); int longestAncestry = 0; // "ancestry" as a length means distance to organization tree root int shortestAncestry = 999; Organization topOrg = null; Organization lowOrg = null; DateTime currentExpiry = DateTime.MinValue; foreach (Membership membership in memberships) { if (membership.Organization.AutoAssignNewMembers) { Organizations ancestry = membership.Organization.GetLine(); if (ancestry.Count > longestAncestry) { longestAncestry = ancestry.Count; lowOrg = membership.Organization; remindermail.pOrgName = lowOrg.MailPrefixInherited; } if (ancestry.Count < shortestAncestry) { shortestAncestry = ancestry.Count; topOrg = membership.Organization; } } if (membership.OrganizationId == Organization.PPSEid) { topOrg = membership.Organization; remindermail.pOrgName = membership.Organization.MailPrefixInherited; currentExpiry = membership.Expires; } } DateTime newExpiry = currentExpiry; if (newExpiry < DateTime.Today.AddYears(10)) { //do not mess with lifetime memberships newExpiry = newExpiry.AddYears(1); if (newExpiry > DateTime.Today.AddYears(1)) { newExpiry = DateTime.Today.AddYears(1).AddDays(1); } } //Person sender = Person.FromIdentity(1); //Rick remindermail.pExpirationDate = currentExpiry; remindermail.pNextDate = newExpiry; remindermail.pPreamble = "<p> Vi är glada att du vill <strong>förnya ditt medlemskap</strong> i Piratpartiet och/eller Ung Pirat.<br /><br />Använd en av länkarna nedan så genomförs förnyelsen.<br />"; string tokenBase = person.PasswordHash + "-" + currentExpiry.Year.ToString(); Organization expectedLowOrg = Organizations.GetMostLocalOrganization(person.GeographyId, Organization.UPSEid); int ageThisYear = DateTime.Now.Year - person.Birthdate.Year; //Hardcoded: age = 26 if (ageThisYear >= 26 && lowOrg.Inherits(Organization.UPSEid)) { // If this person is older than 26, suggest that they leave UP. remindermail.pCurrentAge = ageThisYear.ToString(); remindermail.pCurrentOrg = lowOrg.Name; remindermail.pOtherOrg = topOrg.Name; string link = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?MemberId=" + person.Identity.ToString() + "&Leave=" + lowOrg.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase + "-Leave" + lowOrg.Identity.ToString()). Replace(" ", "").Substring(0, 8); remindermail.pOtherRenewLink = link; remindermail.pWrongOrgSpan = " "; //clear the other span } else if (lowOrg.Inherits(Organization.UPSEid) && lowOrg.Identity != expectedLowOrg.Identity) { // Is this person in the wrong locale? remindermail.pCurrentOrg = lowOrg.Name; remindermail.pOtherOrg = expectedLowOrg.Name; remindermail.pGeographyName = person.Geography.Name; //mailBody += "Du är medlem i " + lowOrg.Name + ", men när du bor i [b]" + person.Geography.Name + // "[/b] så rekommenderar " + // "vi att du byter till din lokala organisation, [b]" + expectedLowOrg.Name + // "[/b]. Klicka här för att göra det:\r\n\r\n"; string link = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?MemberId=" + person.Identity.ToString() + "&Transfer=" + lowOrg.Identity.ToString() + "," + expectedLowOrg.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase + "-Transfer" + lowOrg.Identity.ToString() + "/" + expectedLowOrg.Identity.ToString()).Replace(" ", "").Substring(0, 8); remindermail.pOtherRenewLink = link; remindermail.pTooOldForYouthOrgSpan = " "; //clear the other span //mailBody += "[a href=\"" + link + "\"]" + link + "[/a]\r\n\r\n" + // "Det är naturligtvis inget krav, utan du kan fortsätta precis som förut om du vill. " + // "För att fortsätta i dina befintliga föreningar, klicka här:\r\n\r\n"; } else { remindermail.pTooOldForYouthOrgSpan = " "; //clear the other span remindermail.pWrongOrgSpan = " "; //clear the other span } string stdLink = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?MemberId=" + person.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase).Replace(" ", "").Substring(0, 8); remindermail.pStdRenewLink = stdLink; OutboundMail mail = remindermail.CreateFunctionalOutboundMail(MailAuthorType.MemberService, OutboundMail.PriorityNormal, topOrg, Geography.Root); if (mail.Body.Trim() == "") { throw new Exception("Failed to create a mailBody"); } else { mail.AddRecipient(person.Identity, false); mail.SetRecipientCount(1); mail.SetResolved(); mail.SetReadyForPickup(); PWLog.Write(PWLogItem.Person, person.Identity, PWLogAction.MembershipRenewReminder, "Mail was sent to " + person.Email + " on request to renew membership.", string.Empty); } }
private static void CheckOneBlog(string readerFeedUrl, int personId) { try { Person sender = Person.FromIdentity(personId); string senderName = sender.Name + " (Piratpartiet)"; string senderAddress = sender.PartyEmail; if (personId == 5) { senderName = "=?utf-8?Q?Christian_Engstr=C3=B6m_(Piratpartiet)?="; // compensate for Mono bug } if (personId == 1) { senderAddress = "*****@*****.**"; } RssReader reader = new RssReader(readerFeedUrl); People recipients = null; DateTime highWaterMark = DateTime.MinValue; string persistenceKey = "Newsletter-Highwater-" + personId.ToString(); string highWaterMarkString = Persistence.Key[persistenceKey]; try { highWaterMark = DateTime.Parse(highWaterMarkString); } catch (FormatException) { highWaterMark = DateTime.MinValue; } catch (Exception e) { throw new ReaderException("feed:" + readerFeedUrl, e); } try { Rss rss = reader.Read(); // TODO: Read the high water mark from db foreach (RssChannelItem item in rss.Channel.Items) { // Ignore any items older than the highwater mark. if (item.PubDate < highWaterMark) { continue; } // For each item, look for the "Nyhetsbrev" category. bool publish = false; foreach (RssCategory category in item.Categories) { if (category.Name.ToLowerInvariant() == "nyhetsbrev") { publish = true; } } if (publish) { // Set highwater datetime mark. We do this first as a defense against mail floods, should something go wrong. Persistence.Key[persistenceKey] = item.PubDate.AddMinutes(5).ToString(); // Verify that it was written correctly to database. This is defensive programming to avoid a mail flood. if (DateTime.Parse(Persistence.Key[persistenceKey]) < item.PubDate) { throw new Exception( "Unable to commit new highwater mark to database in NewsletterChecker.Run()"); } bool testMode = false; if (item.Title.ToLower().Contains("test")) { // Newsletter blog entry contains "test" in title -> testmode testMode = true; } // Post to forum string forumText = Blog2Forum(item.Content); string mailText = Blog2Mail(item.Content); int forumPostId = 0; try { forumPostId = SwedishForumDatabase.GetDatabase().CreateNewPost( testMode ? ForumIdTestPost : ForumIdNewsletter, sender, "Nyhetsbrev " + DateTime.Today.ToString("yyyy-MM-dd"), item.Title, forumText); } catch (Exception ex) { if (!System.Diagnostics.Debugger.IsAttached) { // ignore when debugging throw ex; } } // Establish people to send to, if not already done. if (recipients == null || recipients.Count == 1) { recipients = People.FromNewsletterFeed(NewsletterFeed.TypeID.ChairmanBlog); } /* * Disabled sending to activists -- this was done leading up to the election in 2009 */ // Add activists (HACK) // Should probably be better to select by organization, not geography. /* * People activists = Activists.FromGeography(Country.FromCode("SE").Geography).People; * * recipients = recipients.LogicalOr(activists);*/ // OVERRIDE: If this is a TEST newsletter, send ONLY to the originator if (testMode) { recipients = People.FromSingle(sender); item.Title += " [TEST MODE]"; } //TODO: hardcoded Org & geo ... using PP & World Organization org = Organization.PPSE; Geography geo = Geography.Root; NewsletterMail newslettermail = new NewsletterMail(); newslettermail.pSubject = item.Title; newslettermail.pDate = DateTime.Now; newslettermail.pForumPostUrl = String.Format("http://vbulletin.piratpartiet.se/showthread.php?t={0}", forumPostId); newslettermail.pBodyContent = Blog2Mail(item.Content); newslettermail.pOrgName = org.MailPrefixInherited; OutboundMail newMail = newslettermail.CreateOutboundMail(sender, OutboundMail.PriorityLow, org, geo); int recipientCount = 0; foreach (Person recipient in recipients) { if (!Formatting.ValidateEmailFormat(recipient.Mail) || recipient.MailUnreachable || recipient.NeverMail) { continue; } ++recipientCount; newMail.AddRecipient(recipient, false); } newMail.SetRecipientCount(recipientCount); newMail.SetResolved(); newMail.SetReadyForPickup(); } } } finally { reader.Close(); } } catch (Exception ex) { lock (feedErrorSignaled) { if (!feedErrorSignaled.ContainsKey(readerFeedUrl) || feedErrorSignaled[readerFeedUrl].AddMinutes(60) < DateTime.Now) { feedErrorSignaled[readerFeedUrl] = DateTime.Now; throw new ReaderException("NewsletterChecker got error " + ex.Message + "\n when checking feed:" + readerFeedUrl + "\n(feed will be continually checked, bu will not signal error again for an hour)", ex); } } } }
public static void SendChangeOrgMail(Person person, Membership membership, Organization newOrg) { // HACK for UPSE // This is a hack for the Swedish structure. ChangeOrgMail changeorgmail = new ChangeOrgMail(); Organization topOrg = Organization.FromIdentity(Organization.UPSEid); Organization lowOrg = membership.Organization; DateTime currentExpiry = membership.Expires; DateTime newExpiry = DateTime.Today.AddYears(1); changeorgmail.pCurrentOrg = lowOrg.Name; changeorgmail.pCurrentGeo = person.Geography.Name; changeorgmail.pNextDate = newExpiry; string tokenBase = person.PasswordHash + "-" + membership.Identity.ToString() + "-" + currentExpiry.Year.ToString(); if (newOrg.AnchorGeographyId == Geography.RootIdentity && newOrg.AutoAssignNewMembers) {//Fallback org changeorgmail.pNoLocalOrg = newOrg.Name; changeorgmail.pChangeOrg = ""; } else { changeorgmail.pNoLocalOrg = ""; changeorgmail.pChangeOrg = newOrg.Name; } if (lowOrg.AcceptsMembers) { changeorgmail.pInactiveOrg = ""; changeorgmail.pInactiveEnding = " "; } else { changeorgmail.pInactiveOrg = newOrg.Name; } changeorgmail.pStdRenewLink = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?PersonId=" + person.Identity.ToString() + "&Transfer=" + lowOrg.Identity.ToString() + "," + newOrg.Identity.ToString() + "&MembershipId=" + membership.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase + "-Transfer" + lowOrg.Identity.ToString() + "/" + newOrg.Identity.ToString()).Replace(" ", "").Substring(0, 8); OutboundMail mail = changeorgmail.CreateFunctionalOutboundMail(MailAuthorType.MemberService, OutboundMail.PriorityNormal, topOrg, Geography.Root); string test = mail.RenderHtml(person, person.PreferredCulture); test = mail.RenderText(person, person.PreferredCulture); if (mail.Body.Trim() == "") { throw new InvalidOperationException("Failed to create a mailBody"); } else { //mail.AddRecipient(7838, true); mail.AddRecipient(person.Identity, false); mail.SetRecipientCount(1); mail.SetResolved(); mail.SetReadyForPickup(); } }
public static void SendReminderMail(Membership membership) { // First, determine the organization template to use. Prioritize a long ancestry. // This is a hack for the Swedish structure. ReminderMail remindermail = new ReminderMail(); // NEW December 2010: Organizations are separated as per common agreement, there are no common reminder mails. Every membership renews on its own. Organization lowOrg = membership.Organization; DateTime currentExpiry = membership.Expires; Person person = membership.Person; DateTime newExpiry = currentExpiry; //do not mess with lifetime memberships (100 years) if (newExpiry < DateTime.Today.AddYears(10)) { newExpiry = newExpiry.AddYears(1); } remindermail.pPreamble = "<p> Nu är det dags att <strong>förnya ditt medlemskap</strong> i " + membership.Organization.Name + "."; remindermail.pExpirationDate = currentExpiry; remindermail.pNextDate = newExpiry; remindermail.pOrgName = membership.Organization.MailPrefixInherited; string tokenBase = person.PasswordHash + "-" + membership.Identity.ToString() + "-" + currentExpiry.Year.ToString(); // REMOVED DEC2010: suggestion that people older than 25 may leave UP, as renewal mails are separated // HACK for UPSE: Organization expectedLowOrg = Organizations.GetMostLocalOrganization(person.GeographyId, Organization.UPSEid); if (expectedLowOrg != null && lowOrg.Inherits(Organization.UPSEid) && lowOrg.Identity != expectedLowOrg.Identity) { // Is this person in the wrong locale? remindermail.pCurrentOrg = lowOrg.Name; remindermail.pOtherOrg = expectedLowOrg.Name; remindermail.pGeographyName = person.Geography.Name; //mailBody += "Du är medlem i " + lowOrg.Name + ", men när du bor i [b]" + person.Geography.Name + // "[/b] så rekommenderar " + // "vi att du byter till din lokala organisation, [b]" + expectedLowOrg.Name + // "[/b]. Klicka här för att göra det:\r\n\r\n"; string link = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?PersonId=" + person.Identity.ToString() + "&Transfer=" + lowOrg.Identity.ToString() + "," + expectedLowOrg.Identity.ToString() + "&MembershipId=" + membership.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase + "-Transfer" + lowOrg.Identity.ToString() + "/" + expectedLowOrg.Identity.ToString()).Replace(" ", "").Substring(0, 8); remindermail.pOtherRenewLink = link; remindermail.pTooOldForYouthOrgSpan = " "; //clear the other span //mailBody += "[a href=\"" + link + "\"]" + link + "[/a]\r\n\r\n" + // "Det är naturligtvis inget krav, utan du kan fortsätta precis som förut om du vill. " + // "För att fortsätta i dina befintliga föreningar, klicka här:\r\n\r\n"; } else { remindermail.pTooOldForYouthOrgSpan = " "; //clear the other span remindermail.pWrongOrgSpan = " "; //clear the other span //mailBody += "Klicka på den här länken för att förnya för ett år till:\r\n\r\n"; } string stdLink = "https://pirateweb.net/Pages/Public/SE/People/MemberRenew.aspx?PersonId=" + person.Identity.ToString() + "&MembershipId=" + membership.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase).Replace(" ", "").Substring(0, 8); remindermail.pStdRenewLink = stdLink; tokenBase = person.PasswordHash + "-" + membership.Identity.ToString(); string terminateLink = "https://pirateweb.net/Pages/Public/SE/People/MemberTerminate.aspx?MemberId=" + person.Identity.ToString() + "&SecHash=" + SHA1.Hash(tokenBase).Replace(" ", "").Substring(0, 8) + "&MID=" + membership.Identity.ToString(); remindermail.pTerminateLink = terminateLink; //mailBody += "[a href=\"" + stdLink + "\"]" + stdLink + "[/a]\r\n\r\n" + // "[br]Välkommen att vara med oss i [b]ett år till![/b]\r\n\r\n" + // "Hälsningar,[br]Medlemsservice\r\n\r\n"; // + /*"PS: [b]Du fick ett likadant mail alldeles nyss. Om du har å, ä eller ö i ditt namn fungerade " + * "inte länken i det mailet. Tack till alla som hörde av sig om det; felet är fixat nu och länken ovan ska fungera.[/b]\r\n\r\n";*/ //OutboundMail mail = remindermail.CreateOutboundMail(sender, OutboundMail.PriorityNormal, topOrg, Geography.Root); OutboundMail mail = remindermail.CreateFunctionalOutboundMail(MailAuthorType.MemberService, OutboundMail.PriorityNormal, membership.Organization, Geography.Root); if (mail.Body.Trim() == "") { throw new InvalidOperationException("Failed to create a mailBody"); } else { mail.AddRecipient(person.Identity, false); mail.SetRecipientCount(2); mail.SetResolved(); mail.SetReadyForPickup(); } }