PageRenderTime 97ms CodeModel.GetById 10ms app.highlight 78ms RepoModel.GetById 1ms app.codeStats 0ms

/CmsData/Emailer.cs

https://bitbucket.org/mahalowe/bvcms
C# | 1054 lines | 973 code | 69 blank | 12 comment | 168 complexity | b2760617bffde7437bf43ce8758416c8 MD5 | raw file
   1/* Author: David Carroll
   2 * Copyright (c) 2008, 2009 Bellevue Baptist Church 
   3 * Licensed under the GNU General Public License (GPL v2)
   4 * you may not use this code except in compliance with the License.
   5 * You may obtain a copy of the License at http://bvcms.codeplex.com/license 
   6 */
   7using System;
   8using System.Net.Mail;
   9using System.Threading;
  10using CmsData.Codes;
  11using UtilityExtensions;
  12using System.Linq;
  13using System.Text;
  14using System.Configuration;
  15using System.Collections.Generic;
  16using System.Web;
  17using System.Text.RegularExpressions;
  18using System.Diagnostics;
  19using System.IO;
  20using System.Xml.Linq;
  21using HtmlAgilityPack;
  22
  23namespace CmsData
  24{
  25    public partial class CMSDataContext
  26    {
  27        public string CmsHost
  28        {
  29            get
  30            {
  31                var h = ConfigurationManager.AppSettings["cmshost"];
  32                return h.Replace("{church}", Host);
  33            }
  34        }
  35        public void Email(string from, Person p, string subject, string body)
  36        {
  37            Email(from, p, null, subject, body, false);
  38        }
  39        public void EmailRedacted(string from, Person p, string subject, string body)
  40        {
  41            Email(from, p, null, subject, body, true);
  42        }
  43        public void Email(string from, Person p, List<MailAddress> addmail, string subject, string body, bool redacted)
  44        {
  45            var From = Util.FirstAddress(from);
  46            if (From == null)
  47                From = Util.FirstAddress(Util.SysFromEmail);
  48            var emailqueue = new EmailQueue
  49            {
  50                Queued = DateTime.Now,
  51                FromAddr = From.Address,
  52                FromName = From.DisplayName,
  53                Subject = subject,
  54                Body = body,
  55                QueuedBy = Util.UserPeopleId,
  56                Redacted = redacted,
  57                Transactional = true
  58            };
  59            EmailQueues.InsertOnSubmit(emailqueue);
  60            string addmailstr = null;
  61            if (addmail != null)
  62                addmailstr = addmail.EmailAddressListToString();
  63            emailqueue.EmailQueueTos.Add(new EmailQueueTo
  64            {
  65                PeopleId = p.PeopleId,
  66                OrgId = CurrentOrgId,
  67                AddEmail = addmailstr,
  68                Guid = Guid.NewGuid(),
  69            });
  70            SubmitChanges();
  71            SendPersonEmail(CmsHost, emailqueue.Id, p.PeopleId);
  72        }
  73
  74        public List<MailAddress> PersonListToMailAddressList(IEnumerable<Person> list)
  75        {
  76            var aa = new List<MailAddress>();
  77            foreach (var p in list)
  78                aa.AddRange(GetAddressList(p));
  79            return aa;
  80        }
  81        public void Email(string from, IEnumerable<Person> list, string subject, string body)
  82        {
  83            if (list.Count() == 0)
  84                return;
  85            var aa = PersonListToMailAddressList(list);
  86            Email(from, list.First(), aa, subject, body, false);
  87        }
  88        public void EmailRedacted(string from, IEnumerable<Person> list, string subject, string body)
  89        {
  90            if (list.Count() == 0)
  91                return;
  92            var aa = PersonListToMailAddressList(list);
  93            Email(from, list.First(), aa, subject, body, redacted: true);
  94        }
  95        public IEnumerable<Person> PeopleFromPidString(string pidstring)
  96        {
  97            var a = pidstring.SplitStr(",").Select(ss => ss.ToInt()).ToArray();
  98            var q = from p in People
  99                    where a.Contains(p.PeopleId)
 100                    orderby p.PeopleId == a[0] descending
 101                    select p;
 102            return q;
 103        }
 104        public List<Person> StaffPeopleForDiv(int divid)
 105        {
 106            var q = from o in Organizations
 107                    where o.DivOrgs.Any(dd => dd.DivId == divid)
 108                    where o.NotifyIds != null && o.NotifyIds != ""
 109                    select o.NotifyIds;
 110            var pids = string.Join(",", q);
 111            var a = pids.SplitStr(",").Select(ss => ss.ToInt()).ToArray();
 112            var q2 = from p in People
 113                     where a.Contains(p.PeopleId)
 114                     orderby p.PeopleId == a[0] descending
 115                     select p;
 116            if (q2.Count() == 0)
 117                return AdminPeople();
 118            return q2.ToList();
 119        }
 120        public List<Person> AdminPeople()
 121        {
 122            return (from p in CMSRoleProvider.provider.GetAdmins()
 123                    orderby p.Users.Any(u => u.Roles.Contains("Developer")) ascending
 124                    select p).ToList();
 125        }
 126        public List<Person> FinancePeople()
 127        {
 128            var q = from u in Users
 129                    where u.UserRoles.Any(ur => ur.Role.RoleName == "Finance")
 130                    select u.Person;
 131            return q.ToList();
 132        }
 133        public string StaffEmailForOrg(int orgid)
 134        {
 135            var q = from o in Organizations
 136                    where o.OrganizationId == orgid
 137                    where o.NotifyIds != null && o.NotifyIds != ""
 138                    select o.NotifyIds;
 139            var pids = string.Join(",", q);
 140            var a = pids.SplitStr(",").Select(ss => ss.ToInt()).ToArray();
 141            var q2 = from p in People
 142                     where p.PeopleId == a[0]
 143                     select p.FromEmail;
 144            if (q2.Count() == 0)
 145                return (from p in CMSRoleProvider.provider.GetAdmins()
 146                        orderby p.Users.Any(u => u.Roles.Contains("Developer")) descending
 147                        select p.FromEmail).First();
 148            return q2.SingleOrDefault();
 149        }
 150        public List<Person> StaffPeopleForOrg(int orgid)
 151        {
 152            var org = LoadOrganizationById(orgid);
 153            var pids = org.NotifyIds ?? "";
 154            var a = pids.Split(',').Select(ss => ss.ToInt()).ToArray();
 155            var q2 = from p in People
 156                     where a.Contains(p.PeopleId)
 157                     orderby p.PeopleId == a.FirstOrDefault() descending
 158                     select p;
 159            //if (q2.Count() == 0)
 160            //    return (from p in CMSRoleProvider.provider.GetAdmins()
 161            //            orderby p.Users.Any(u => u.Roles.Contains("Developer")) descending
 162            //            select p).ToList();
 163            return q2.ToList();
 164        }
 165        public Person UserPersonFromEmail(string email)
 166        {
 167            var q = from u in Users
 168                    where u.Person.EmailAddress == email || u.Person.EmailAddress2 == email
 169                    select u.Person;
 170            var p = q.FirstOrDefault();
 171            if (p == null)
 172                p = CMSRoleProvider.provider.GetAdmins().First();
 173            return p;
 174        }
 175        public EmailQueue CreateQueue(MailAddress From, string subject, string body, DateTime? schedule, int tagId, bool publicViewable)
 176        {
 177            var emailqueue = new EmailQueue
 178            {
 179                Queued = DateTime.Now,
 180                FromAddr = From.Address,
 181                FromName = From.DisplayName,
 182                Subject = subject,
 183                Body = body,
 184                SendWhen = schedule,
 185                QueuedBy = Util.UserPeopleId,
 186                Transactional = false,
 187                PublicX = publicViewable,
 188            };
 189            EmailQueues.InsertOnSubmit(emailqueue);
 190
 191            SubmitChanges();
 192            if (body.Contains("http://publiclink", ignoreCase: true))
 193            {
 194                var link = Util.URLCombine(CmsHost, "/Manage/Emails/View/" + emailqueue.Id);
 195                var re = new Regex("http://publiclink", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 196                emailqueue.Body = re.Replace(body, link);
 197            }
 198
 199            var tag = TagById(tagId);
 200            var q = tag.People(this); 
 201
 202            var q2 = from p in q.Distinct()
 203                     where p.EmailAddress != null
 204                     where p.EmailAddress != ""
 205                     where (p.SendEmailAddress1 ?? true) || (p.SendEmailAddress2 ?? false)
 206                     where !p.EmailOptOuts.Any(oo => oo.FromEmail == emailqueue.FromAddr)
 207                     orderby p.PeopleId
 208                     select p.PeopleId;
 209
 210            var i = 0;
 211            foreach (var pid in q2)
 212            {
 213                i++;
 214                emailqueue.EmailQueueTos.Add(new EmailQueueTo
 215                {
 216                    PeopleId = pid,
 217                    OrgId = CurrentOrgId,
 218                    Guid = Guid.NewGuid()
 219                });
 220            }
 221            SubmitChanges();
 222            return emailqueue;
 223        }
 224        public void SendPersonEmail(string CmsHost, int id, int pid)
 225        {
 226            var SysFromEmail = Setting("SysFromEmail", ConfigurationManager.AppSettings["sysfromemail"]);
 227            var emailqueue = EmailQueues.Single(eq => eq.Id == id);
 228            var emailqueueto = EmailQueueTos.Single(eq => eq.Id == id && eq.PeopleId == pid);
 229            var fromname = emailqueue.FromName;
 230            if (!fromname.HasValue())
 231                fromname = emailqueue.FromAddr;
 232            else
 233                fromname = emailqueue.FromName.Replace("\"", "");
 234            var From = Util.FirstAddress(emailqueue.FromAddr, fromname);
 235
 236            try
 237            {
 238                var p = LoadPersonById(emailqueueto.PeopleId);
 239                string text = emailqueue.Body;
 240                var aa = DoReplacements(ref text, CmsHost, p, emailqueueto);
 241
 242                var qs = "OptOut/UnSubscribe/?enc=" + Util.EncryptForUrl("{0}|{1}".Fmt(emailqueueto.PeopleId, From.Address));
 243                var url = Util.URLCombine(CmsHost, qs);
 244                var link = @"<a href=""{0}"">Unsubscribe</a>".Fmt(url);
 245                text = text.Replace("{unsubscribe}", link);
 246                text = text.Replace("{Unsubscribe}", link);
 247                if (aa.Count > 0)
 248                {
 249                    text = text.Replace("{toemail}", aa[0].Address);
 250                    text = text.Replace("%7Btoemail%7D", aa[0].Address);
 251                }
 252                text = text.Replace("{fromemail}", From.Address);
 253                text = text.Replace("%7Bfromemail%7D", From.Address);
 254
 255                if (Setting("sendemail", "true") != "false")
 256                {
 257                    if (aa.Count > 0)
 258                        Util.SendMsg(SysFromEmail, CmsHost, From, emailqueue.Subject, text, aa, emailqueue.Id, pid);
 259                    else
 260                        Util.SendMsg(SysFromEmail, CmsHost, From,
 261                            "(no email address) " + emailqueue.Subject,
 262                            "<p style='color:red'>You are receiving this because there is no email address for {0}({1}). You should probably contact them since they were probably expecting this information.</p>\n{2}".Fmt(p.Name, p.PeopleId, text),
 263                            Util.ToMailAddressList(From),
 264                            emailqueue.Id, pid);
 265                    emailqueueto.Sent = DateTime.Now;
 266                    emailqueue.Sent = DateTime.Now;
 267                    if (emailqueue.Redacted ?? false)
 268                        emailqueue.Body = "redacted";
 269                    SubmitChanges();
 270                }
 271            }
 272            catch (Exception ex)
 273            {
 274                Util.SendMsg(SysFromEmail, CmsHost, From,
 275                    "sent emails - error", ex.ToString(),
 276                    Util.ToMailAddressList(From),
 277                    emailqueue.Id, null);
 278                throw ex;
 279            }
 280        }
 281        public List<MailAddress> DoReplacements(ref string text, string CmsHost, Person p, EmailQueueTo emailqueueto)
 282        {
 283            if (text == null)
 284                text = "(no content)";
 285            if (p.Name.Contains("?") || p.Name.Contains("unknown", true))
 286                text = text.Replace("{name}", string.Empty);
 287            else
 288                text = text.Replace("{name}", p.Name);
 289
 290            if (p.PreferredName.Contains("?", true) || (p.PreferredName.Contains("unknown", true)))
 291                text = text.Replace("{first}", string.Empty);
 292            else
 293                text = text.Replace("{first}", p.PreferredName);
 294            text = text.Replace("{occupation}", p.OccupationOther);
 295            var eurl = Util.URLCombine(CmsHost, "Manage/Emails/View/" + emailqueueto.Id);
 296            text = text.Replace("{emailhref}", eurl);
 297
 298            text = DoVoteLinkAnchorStyle(text, CmsHost, emailqueueto);
 299            text = DoVoteTag(text, CmsHost, emailqueueto);
 300            text = DoVoteTag2(text, CmsHost, emailqueueto);
 301            text = DoRegisterTag(text, CmsHost, emailqueueto);
 302            text = DoRegisterTag2(text, CmsHost, emailqueueto);
 303            text = DoExtraValueData(text, emailqueueto);
 304            if (emailqueueto.OrgId.HasValue)
 305            {
 306                if (text.Contains("{smallgroup:"))
 307                    text = DoSmallGroupData(text, emailqueueto);
 308                if (text.Contains("{addsmallgroup:"))
 309                    text = DoAddSmallGroup(text, emailqueueto);
 310                if (text.Contains("{nextmeetingtime}"))
 311                    text = DoMeetingDate(text, emailqueueto);
 312            }
 313            if (text.Contains("{createaccount}"))
 314                text = text.Replace("{createaccount}", DoCreateUserTag(CmsHost, emailqueueto));
 315            if (text.Contains("http://votelink", ignoreCase: true))
 316                text = DoVoteLink(text, CmsHost, emailqueueto);
 317            if (text.Contains("http://registerlink", ignoreCase: true))
 318                text = DoRegisterLink(text, CmsHost, emailqueueto);
 319            if (text.Contains("http://rsvplink", ignoreCase: true))
 320                text = DoRsvpLink(text, CmsHost, emailqueueto);
 321            if (text.Contains("http://volsublink", ignoreCase: true))
 322                text = DoVolSubLink(text, CmsHost, emailqueueto);
 323            if (text.Contains("http://volreqlink", ignoreCase: true))
 324                text = DoVolReqLink(text, CmsHost, emailqueueto);
 325            if (text.Contains("{barcode}", ignoreCase: true))
 326            {
 327                var link = Util.URLCombine(CmsHost, "/Track/Barcode/" + emailqueueto.PeopleId);
 328                text = text.Replace("{barcode}", "<img src='" + link + "' />");
 329            }
 330            if (text.Contains("{campus}", ignoreCase: true))
 331                if (p.CampusId != null)
 332                    text = text.Replace("{campus}", p.Campu.Description);
 333                else
 334                    text = text.Replace("{campus}", "No Campus Specified");
 335
 336            if (emailqueueto.Guid.HasValue)
 337            {
 338                var turl = Util.URLCombine(CmsHost, "/Track/Key/" + emailqueueto.Guid.Value.GuidToQuerystring());
 339                text = text.Replace("{track}", "<img src=\"{0}\" />".Fmt(turl));
 340            }
 341
 342            var aa = GetAddressList(p);
 343
 344            if (emailqueueto.AddEmail.HasValue())
 345                foreach (var ad in emailqueueto.AddEmail.SplitStr(","))
 346                    Util.AddGoodAddress(aa, ad);
 347
 348            if (emailqueueto.OrgId.HasValue)
 349            {
 350                var qm = (from m in OrganizationMembers
 351                          where m.PeopleId == emailqueueto.PeopleId && m.OrganizationId == emailqueueto.OrgId
 352                          select new { m.PayLink, m.Amount, m.AmountPaid, m.RegisterEmail }).SingleOrDefault();
 353                if (qm != null)
 354                {
 355                    if (qm.PayLink.HasValue())
 356                        text = text.Replace("{paylink}", "<a href=\"{0}\">payment link</a>".Fmt(qm.PayLink));
 357                    text = text.Replace("{amtdue}", (qm.Amount - qm.AmountPaid).ToString2("c"));
 358                    Util.AddGoodAddress(aa, Util.FullEmail(qm.RegisterEmail, p.Name));
 359                }
 360            }
 361            return aa.DistinctEmails();
 362        }
 363        private string DoMeetingDate(string text, EmailQueueTo emailqueueto)
 364        {
 365            var mt = (from aa in Attends
 366                      where aa.OrganizationId == emailqueueto.OrgId
 367                      where aa.PeopleId == emailqueueto.PeopleId
 368                      where aa.Commitment == AttendCommitmentCode.Attending
 369                      where aa.MeetingDate > DateTime.Now
 370                      orderby aa.MeetingDate
 371                      select aa.MeetingDate).FirstOrDefault();
 372            text = text.Replace("{nextmeetingtime}", mt.ToString("g"));
 373            return text;
 374        }
 375        private string DoSmallGroupData(string text, EmailQueueTo emailqueueto)
 376        {
 377            const string RE = @"\{smallgroup:\[(?<prefix>[^\]]*)\](?:,(?<def>[^}]*)){0,1}\}";
 378            var re = new Regex(RE, RegexOptions.Singleline);
 379            var match = re.Match(text);
 380            while (match.Success && emailqueueto.OrgId.HasValue)
 381            {
 382                var tag = match.Value;
 383                var prefix = match.Groups["prefix"].Value;
 384                var def = match.Groups["def"].Value;
 385                var sg = (from mm in OrgMemMemTags
 386                          where mm.OrgId == emailqueueto.OrgId
 387                          where mm.PeopleId == emailqueueto.PeopleId
 388                          where mm.MemberTag.Name.StartsWith(prefix)
 389                          select mm.MemberTag.Name).FirstOrDefault();
 390                if (!sg.HasValue())
 391                    sg = def;
 392                text = text.Replace(tag, sg);
 393                match = match.NextMatch();
 394            }
 395            return text;
 396        }
 397        private string DoAddSmallGroup(string text, EmailQueueTo emailqueueto)
 398        {
 399            const string RE = @"\{addsmallgroup:\[(?<group>[^\]]*)\]\}";
 400            var re = new Regex(RE, RegexOptions.Singleline);
 401            var match = re.Match(text);
 402            if (match.Success && emailqueueto.OrgId.HasValue)
 403            {
 404                var tag = match.Value;
 405                var group = match.Groups["group"].Value;
 406                var om = (from mm in OrganizationMembers
 407                          where mm.OrganizationId == emailqueueto.OrgId
 408                          where mm.PeopleId == emailqueueto.PeopleId
 409                          select mm).SingleOrDefault();
 410                if (om != null)
 411                    om.AddToGroup(this, group);
 412                text = text.Replace(tag, "");
 413            }
 414            return text;
 415        }
 416        private string DoExtraValueData(string text, EmailQueueTo emailqueueto)
 417        {
 418            const string RE = @"{extra(?<type>.*?):(?<field>.*?)}";
 419            var re = new Regex(RE, RegexOptions.Singleline);
 420            var match = re.Match(text);
 421            while (match.Success)
 422            {
 423                var tag = match.Value;
 424                var field = match.Groups["field"].Value;
 425                var type = match.Groups["type"].Value;
 426                var ev = PeopleExtras.SingleOrDefault(ee => ee.Field == field && emailqueueto.PeopleId == ee.PeopleId);
 427                string value = "";
 428                switch (type)
 429                {
 430                    case "value":
 431                        value = ev.StrValue;
 432                        break;
 433                    case "data":
 434                        value = ev.Data;
 435                        break;
 436                    case "date":
 437                        value = ev.DateValue.FormatDate();
 438                        break;
 439                    case "int":
 440                        value = ev.IntValue.ToString();
 441                        break;
 442                }
 443                text = text.Replace(tag, value);
 444                match = match.NextMatch();
 445            }
 446            return text;
 447        }
 448        private string DoVoteLinkAnchorStyle(string text, string CmsHost, EmailQueueTo emailqueueto)
 449        {
 450            var list = new Dictionary<string, OneTimeLink>();
 451            const string VoteLinkRE = @"{votelink(?<inside>[^}]*)}";
 452            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline);
 453            var match = re.Match(text);
 454            while (match.Success)
 455            {
 456                var votelink = match.Value;
 457                var anchor = "<a " + match.Groups["inside"].Value + ">text</a>";
 458                anchor = anchor.Replace("&quot;", "\"");
 459                anchor = anchor.Replace("&rdquo;", "\"");
 460                anchor = anchor.Replace("&ldquo;", "\"");
 461
 462                var doc = new HtmlDocument();
 463                doc.LoadHtml(anchor);
 464                var ele = doc.DocumentNode.Element("a");
 465                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 466                var txt = "click here";
 467                if (d.ContainsKey("text"))
 468                    txt = d["text"];
 469
 470                string msg = "Thank you for responding.";
 471                if (d.ContainsKey("message"))
 472                    msg = d["message"];
 473
 474                string confirm = "false";
 475                if (d.ContainsKey("confirm"))
 476                    confirm = d["confirm"];
 477
 478                if (!d.ContainsKey("smallgroup"))
 479                    throw new Exception("Votelink: no smallgroup attribute");
 480                var smallgroup = d["smallgroup"];
 481                var pre = "";
 482                var a = smallgroup.SplitStr(":");
 483                if (a.Length > 1)
 484                    pre = a[0];
 485
 486                if (!d.ContainsKey("id"))
 487                    throw new Exception("Votelink: no id attribute");
 488                var id = d["id"];
 489
 490                var url = VoteLinkUrl(text, CmsHost, emailqueueto, list, votelink, id, msg, confirm, smallgroup, pre);
 491                text = text.Replace(votelink, @"<a href=""{0}"">{1}</a>".Fmt(url, txt));
 492
 493                match = match.NextMatch();
 494            }
 495            return text;
 496        }//&lt;votetag .*?&gt;(?<inside>.+?)&lt;/votetag&gt;
 497        private string DoVoteTag2(string text, string CmsHost, EmailQueueTo emailqueueto)
 498        {
 499            var list = new Dictionary<string, OneTimeLink>();
 500            const string VoteLinkRE = @"&lt;votetag .*?&gt;(?<inside>.+?)&lt;/votetag&gt;";
 501            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline);
 502            var match = re.Match(text);
 503            while (match.Success)
 504            {
 505                var tag = match.Value;
 506                var inside = HttpUtility.HtmlDecode(match.Groups["inside"].Value);
 507
 508                var doc = new HtmlDocument();
 509                doc.LoadHtml(tag);
 510                var ele = doc.DocumentNode.Element("votetag");
 511                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 512
 513                string msg = "Thank you for responding.";
 514                if (d.ContainsKey("message"))
 515                    msg = d["message"];
 516
 517                string confirm = "false";
 518                if (d.ContainsKey("confirm"))
 519                    confirm = d["confirm"];
 520
 521                if (!d.ContainsKey("smallgroup"))
 522                    throw new Exception("Votelink: no smallgroup attribute");
 523                var smallgroup = d["smallgroup"];
 524                var pre = "";
 525                var a = smallgroup.SplitStr(":");
 526                if (a.Length > 1)
 527                    pre = a[0];
 528
 529                if (!d.ContainsKey("id"))
 530                    throw new Exception("Votelink: no id attribute");
 531                var id = d["id"];
 532
 533                var url = VoteLinkUrl(text, CmsHost, emailqueueto, list, tag, id, msg, confirm, smallgroup, pre);
 534                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 535                match = match.NextMatch();
 536            }
 537            return text;
 538        }
 539        private string DoCreateUserTag(string CmsHost, EmailQueueTo emailqueueto)
 540        {
 541            var user = (from u in Users
 542                        where u.PeopleId == emailqueueto.PeopleId
 543                        select u).FirstOrDefault();
 544            if (user != null)
 545            {
 546                user.ResetPasswordCode = Guid.NewGuid();
 547                user.ResetPasswordExpires = DateTime.Now.AddHours(Setting("ResetPasswordExpiresHours", "24").ToInt());
 548                var link = Util.URLCombine(CmsHost, "/Account/SetPassword/" + user.ResetPasswordCode.ToString());
 549                SubmitChanges();
 550                return @"<a href=""{0}"">Set password for {1}</a>".Fmt(link, user.Username);
 551            }
 552            var ot = new OneTimeLink
 553            {
 554                Id = Guid.NewGuid(),
 555                Querystring = emailqueueto.PeopleId.ToString()
 556            };
 557            OneTimeLinks.InsertOnSubmit(ot);
 558            SubmitChanges();
 559            var url = Util.URLCombine(CmsHost, "/Account/CreateAccount/{0}".Fmt(ot.Id.ToCode()));
 560            return @"<a href=""{0}"">Create Account</a>".Fmt(url);
 561        }
 562        private string DoVoteLink(string text, string CmsHost, EmailQueueTo emailqueueto)
 563        {
 564            //<a dir="ltr" href="http://votelink" id="798" rel="smallgroup" title="This is a message">test</a>
 565            var list = new Dictionary<string, OneTimeLink>();
 566            const string VoteLinkRE = "<a[^>]*?href=\"https{0,1}://votelink\"[^>]*>.*?</a>";
 567            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 568            var match = re.Match(text);
 569            while (match.Success)
 570            {
 571                var tag = match.Value;
 572
 573                var doc = new HtmlDocument();
 574                doc.LoadHtml(tag);
 575                var ele = doc.DocumentNode.Element("a");
 576                var inside = ele.InnerHtml;
 577                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 578
 579                string msg = "Thank you for responding.";
 580                if (d.ContainsKey("title"))
 581                    msg = d["title"];
 582
 583                string confirm = "false";
 584                if (d.ContainsKey("dir") && d["dir"] == "ltr")
 585                    confirm = "true";
 586
 587                if (!d.ContainsKey("rel"))
 588                    throw new Exception("Votelink: no smallgroup attribute");
 589                var smallgroup = d["rel"];
 590                var pre = "";
 591                var a = smallgroup.SplitStr(":");
 592                if (a.Length > 1)
 593                    pre = a[0];
 594
 595                if (!d.ContainsKey("id"))
 596                    throw new Exception("Votelink: no id attribute");
 597                var id = d["id"];
 598
 599                var url = VoteLinkUrl(text, CmsHost, emailqueueto, list, tag, id, msg, confirm, smallgroup, pre);
 600                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 601                match = match.NextMatch();
 602            }
 603            return text;
 604        }
 605        private string DoRsvpLink(string text, string CmsHost, EmailQueueTo emailqueueto)
 606        {
 607            //<a dir="ltr" href="http://rsvplink" id="798" rel="meetingid" title="This is a message">test</a>
 608            var list = new Dictionary<string, OneTimeLink>();
 609            const string RsvpLinkRE = "<a[^>]*?href=\"https{0,1}://rsvplink\"[^>]*>.*?</a>";
 610            var re = new Regex(RsvpLinkRE, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 611            var match = re.Match(text);
 612            while (match.Success)
 613            {
 614                var tag = match.Value;
 615
 616                var doc = new HtmlDocument();
 617                doc.LoadHtml(tag);
 618                var ele = doc.DocumentNode.Element("a");
 619                var inside = ele.InnerHtml;
 620                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 621
 622                string msg = "Thank you for responding.";
 623                if (d.ContainsKey("title"))
 624                    msg = d["title"];
 625
 626                string confirm = "false";
 627                if (d.ContainsKey("dir") && d["dir"] == "ltr")
 628                    confirm = "true";
 629
 630                string smallgroup = null;
 631                if (d.ContainsKey("rel"))
 632                    smallgroup = d["rel"];
 633
 634                if (!d.ContainsKey("id"))
 635                    throw new Exception("Rsvplink: no id attribute");
 636                var id = d["id"];
 637
 638                var url = RsvpLinkUrl(CmsHost, emailqueueto, list, id, smallgroup, msg, confirm);
 639                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 640                match = match.NextMatch();
 641            }
 642            return text;
 643        }
 644        private string DoRegisterLink(string text, string CmsHost, EmailQueueTo emailqueueto)
 645        {
 646            var list = new Dictionary<string, OneTimeLink>();
 647            const string VoteLinkRE = "<a[^>]*?href=\"https{0,1}://(?<rlink>registerlink2{0,1})\"[^>]*>.*?</a>";
 648            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 649            var match = re.Match(text);
 650            while (match.Success)
 651            {
 652                var tag = match.Value;
 653                var rlink = match.Groups["rlink"].Value;
 654
 655                var doc = new HtmlDocument();
 656                doc.LoadHtml(tag);
 657                var ele = doc.DocumentNode.Element("a");
 658                var inside = ele.InnerHtml;
 659                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 660
 661                if (!d.ContainsKey("id"))
 662                    throw new Exception("RegisterTag: no id attribute");
 663                var id = d["id"];
 664
 665                var url = RegisterTagUrl(text, CmsHost, emailqueueto, list, tag, id,
 666                    showfamily: rlink == "registerlink2");
 667                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 668                match = match.NextMatch();
 669            }
 670            return text;
 671        }
 672        public string DoVolSubLink(string text, string CmsHost, EmailQueueTo emailqueueto)
 673        {
 674            var list = new Dictionary<string, OneTimeLink>();
 675            const string VolSubLinkRE = "<a[^>]*?href=\"https{0,1}://volsublink\"[^>]*>.*?</a>";
 676            var re = new Regex(VolSubLinkRE, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 677            var match = re.Match(text);
 678            while (match.Success)
 679            {
 680                var tag = match.Value;
 681
 682                var doc = new HtmlDocument();
 683                doc.LoadHtml(tag);
 684                var ele = doc.DocumentNode.Element("a");
 685                var inside = ele.InnerHtml;
 686                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 687
 688                var qs = "{0},{1},{2},{3}"
 689                    .Fmt(d["aid"], d["pid"], d["ticks"], emailqueueto.PeopleId);
 690                OneTimeLink ot = null;
 691                if (list.ContainsKey(qs))
 692                    ot = list[qs];
 693                else
 694                {
 695                    ot = new OneTimeLink
 696                    {
 697                        Id = Guid.NewGuid(),
 698                        Querystring = qs
 699                    };
 700                    OneTimeLinks.InsertOnSubmit(ot);
 701                    SubmitChanges();
 702                    list.Add(qs, ot);
 703                }
 704
 705                var url = Util.URLCombine(CmsHost, "/OnlineReg/ClaimVolSub/{0}/{1}".Fmt(d["ans"], ot.Id.ToCode()));
 706                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 707                match = match.NextMatch();
 708            }
 709            return text;
 710        }
 711        public string DoVolReqLink(string text, string CmsHost, EmailQueueTo emailqueueto)
 712        {
 713            var list = new Dictionary<string, OneTimeLink>();
 714            const string VolSubLinkRE = "<a[^>]*?href=\"https{0,1}://volreqlink\"[^>]*>.*?</a>";
 715            var re = new Regex(VolSubLinkRE, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase);
 716            var match = re.Match(text);
 717            while (match.Success)
 718            {
 719                var tag = match.Value;
 720
 721                var doc = new HtmlDocument();
 722                doc.LoadHtml(tag);
 723                var ele = doc.DocumentNode.Element("a");
 724                var inside = ele.InnerHtml;
 725                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 726
 727                var qs = "{0},{1},{2},{3}"
 728                    .Fmt(d["mid"], d["pid"], d["ticks"], emailqueueto.PeopleId);
 729                OneTimeLink ot = null;
 730                if (list.ContainsKey(qs))
 731                    ot = list[qs];
 732                else
 733                {
 734                    ot = new OneTimeLink
 735                    {
 736                        Id = Guid.NewGuid(),
 737                        Querystring = qs
 738                    };
 739                    OneTimeLinks.InsertOnSubmit(ot);
 740                    SubmitChanges();
 741                    list.Add(qs, ot);
 742                }
 743
 744                var url = Util.URLCombine(CmsHost, "/OnlineReg/RequestResponse?ans={0}&guid={1}".Fmt(d["ans"], ot.Id.ToCode()));
 745                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 746                match = match.NextMatch();
 747            }
 748            return text;
 749        }
 750
 751        private string DoVoteTag(string text, string CmsHost, EmailQueueTo emailqueueto)
 752        {
 753            var list = new Dictionary<string, OneTimeLink>();
 754            const string VoteLinkRE = @"<votetag[^>]*>(?<inside>.+?)</votetag>";
 755            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline);
 756            var match = re.Match(text);
 757            while (match.Success)
 758            {
 759                var tag = match.Value;
 760                var inside = match.Groups["inside"].Value;
 761
 762                var doc = new HtmlDocument();
 763                doc.LoadHtml(tag);
 764                var ele = doc.DocumentNode.Element("votetag");
 765                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 766
 767                string msg = "Thank you for responding.";
 768                if (d.ContainsKey("message"))
 769                    msg = d["message"];
 770
 771                string confirm = "false";
 772                if (d.ContainsKey("confirm"))
 773                    confirm = d["confirm"];
 774
 775                if (!d.ContainsKey("smallgroup"))
 776                    throw new Exception("Votelink: no smallgroup attribute");
 777                var smallgroup = d["smallgroup"];
 778                var pre = "";
 779                var a = smallgroup.SplitStr(":");
 780                if (a.Length > 1)
 781                    pre = a[0];
 782
 783                if (!d.ContainsKey("id"))
 784                    throw new Exception("Votelink: no id attribute");
 785                var id = d["id"];
 786
 787                var url = VoteLinkUrl(text, CmsHost, emailqueueto, list, tag, id, msg, confirm, smallgroup, pre);
 788                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 789                match = match.NextMatch();
 790            }
 791            return text;
 792        }
 793        private string DoRegisterTag2(string text, string CmsHost, EmailQueueTo emailqueueto)
 794        {
 795            var list = new Dictionary<string, OneTimeLink>();
 796            const string VoteLinkRE = @"&lt;registertag .*?&gt;(?<inside>.+?)&lt;/registertag&gt;";
 797            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline);
 798            var match = re.Match(text);
 799            while (match.Success)
 800            {
 801                var tag = match.Value;
 802                var inside = HttpUtility.HtmlDecode(match.Groups["inside"].Value);
 803
 804                var doc = new HtmlDocument();
 805                doc.LoadHtml(tag);
 806                var ele = doc.DocumentNode.Element("registertag");
 807                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 808
 809                if (!d.ContainsKey("id"))
 810                    throw new Exception("RegisterTag: no id attribute");
 811                var id = d["id"];
 812
 813                var url = RegisterTagUrl(text, CmsHost, emailqueueto, list, tag, id);
 814                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 815                match = match.NextMatch();
 816            }
 817            return text;
 818        }
 819        private string DoRegisterTag(string text, string CmsHost, EmailQueueTo emailqueueto)
 820        {
 821            var list = new Dictionary<string, OneTimeLink>();
 822            const string VoteLinkRE = @"<registertag[^>]*>(?<inside>.+?)</registertag>";
 823            var re = new Regex(VoteLinkRE, RegexOptions.Singleline | RegexOptions.Multiline);
 824            var match = re.Match(text);
 825            while (match.Success)
 826            {
 827                var tag = match.Value;
 828                var inside = match.Groups["inside"].Value;
 829
 830                var doc = new HtmlDocument();
 831                doc.LoadHtml(tag);
 832                var ele = doc.DocumentNode.Element("registertag");
 833                var d = ele.Attributes.ToDictionary(aa => aa.Name.ToString(), aa => aa.Value);
 834
 835                if (!d.ContainsKey("id"))
 836                    throw new Exception("RegisterTag: no id attribute");
 837                var id = d["id"];
 838
 839                var url = RegisterTagUrl(text, CmsHost, emailqueueto, list, tag, id);
 840                text = text.Replace(tag, @"<a href=""{0}"">{1}</a>".Fmt(url, inside));
 841                match = match.NextMatch();
 842            }
 843            return text;
 844        }
 845        private string RsvpLinkUrl(
 846            string CmsHost,
 847            EmailQueueTo emailqueueto,
 848            Dictionary<string, OneTimeLink> list,
 849            string id,
 850            string smallgroup,
 851            string msg,
 852            string confirm)
 853        {
 854            var qs = "{0},{1},{2}".Fmt(id, emailqueueto.PeopleId, emailqueueto.Id);
 855            OneTimeLink ot;
 856            if (list.ContainsKey(qs))
 857                ot = list[qs];
 858            else
 859            {
 860                ot = new OneTimeLink
 861                {
 862                    Id = Guid.NewGuid(),
 863                    Querystring = qs
 864                };
 865                OneTimeLinks.InsertOnSubmit(ot);
 866                SubmitChanges();
 867                list.Add(qs, ot);
 868            }
 869            var url = Util.URLCombine(CmsHost, "/OnlineReg/RsvpLink/{0}?confirm={1}&smallgroup={2}&message={3}"
 870                .Fmt(ot.Id.ToCode(), confirm, smallgroup, HttpUtility.UrlEncode(msg)));
 871            return url;
 872        }
 873        private string VoteLinkUrl(string text,
 874            string CmsHost,
 875            EmailQueueTo emailqueueto,
 876            Dictionary<string, OneTimeLink> list,
 877            string votelink,
 878            string id,
 879            string msg,
 880            string confirm,
 881            string smallgroup,
 882            string pre)
 883        {
 884            var qs = "{0},{1},{2},{3}".Fmt(id, emailqueueto.PeopleId, emailqueueto.Id, pre);
 885            OneTimeLink ot;
 886            if (list.ContainsKey(qs))
 887                ot = list[qs];
 888            else
 889            {
 890                ot = new OneTimeLink
 891                {
 892                    Id = Guid.NewGuid(),
 893                    Querystring = qs
 894                };
 895                OneTimeLinks.InsertOnSubmit(ot);
 896                SubmitChanges();
 897                list.Add(qs, ot);
 898            }
 899            var url = Util.URLCombine(CmsHost, "/OnlineReg/VoteLink/{0}?smallgroup={1}&confirm={2}&message={3}"
 900                .Fmt(ot.Id.ToCode(), HttpUtility.UrlEncode(smallgroup), confirm, HttpUtility.UrlEncode(msg)));
 901            return url;
 902        }
 903        private string RegisterTagUrl(string text,
 904            string CmsHost,
 905            EmailQueueTo emailqueueto,
 906            Dictionary<string, OneTimeLink> list,
 907            string votelink,
 908            string id,
 909            bool showfamily = false)
 910        {
 911            var qs = "{0},{1},{2}".Fmt(id, emailqueueto.PeopleId, emailqueueto.Id);
 912            OneTimeLink ot;
 913            if (list.ContainsKey(qs))
 914                ot = list[qs];
 915            else
 916            {
 917                ot = new OneTimeLink
 918                {
 919                    Id = Guid.NewGuid(),
 920                    Querystring = qs
 921                };
 922                OneTimeLinks.InsertOnSubmit(ot);
 923                SubmitChanges();
 924                list.Add(qs, ot);
 925            }
 926            var url = Util.URLCombine(CmsHost, "/OnlineReg/RegisterLink/{0}".Fmt(ot.Id.ToCode()));
 927            if (showfamily)
 928                url += "?showfamily=true";
 929            return url;
 930        }
 931        public List<MailAddress> GetAddressList(Person p)
 932        {
 933            return GetAddressList(p, null);
 934        }
 935        public List<MailAddress> GetAddressList(Person p, string regemail)
 936        {
 937            var aa = new List<MailAddress>();
 938            if (p.SendEmailAddress1 ?? true)
 939                Util.AddGoodAddress(aa, p.FromEmail);
 940            if (p.SendEmailAddress2 ?? false)
 941                Util.AddGoodAddress(aa, p.FromEmail2);
 942            if (regemail.HasValue())
 943                foreach (var ad in regemail.SplitStr(",;"))
 944                    Util.AddGoodAddress(aa, ad);
 945            return aa;
 946        }
 947
 948        bool EmailMatch(string existing, string addemail)
 949        {
 950            var exist = Util.TryGetMailAddress(existing, null);
 951            var add = Util.TryGetMailAddress(addemail, null);
 952            if (add == null || exist == null)
 953                return false;
 954            var r = string.Compare(exist.Address, add.Address, true);
 955            return r == 0;
 956        }
 957        public void SendPeopleEmail(int queueid)
 958        {
 959            var emailqueue = EmailQueues.Single(ee => ee.Id == queueid);
 960            var sysFromEmail = Setting("SysFromEmail", ConfigurationManager.AppSettings["sysfromemail"]);
 961            var From = Util.FirstAddress(emailqueue.FromAddr, emailqueue.FromName);
 962            if (!emailqueue.Subject.HasValue() || !emailqueue.Body.HasValue())
 963            {
 964                Util.SendMsg(sysFromEmail, CmsHost, From,
 965                    "sent emails - error", "no subject or body, no emails sent",
 966                    Util.ToMailAddressList(From),
 967                    emailqueue.Id, null);
 968                return;
 969            }
 970
 971            emailqueue.Started = DateTime.Now;
 972            SubmitChanges();
 973
 974            var q = from To in EmailQueueTos
 975                    where To.Id == emailqueue.Id
 976                    where To.Sent == null
 977                    orderby To.PeopleId
 978                    select To;
 979            foreach (var To in q)
 980            {
 981                try
 982                {
 983                    var p = LoadPersonById(To.PeopleId);
 984                    string text = emailqueue.Body;
 985                    var aa = DoReplacements(ref text, CmsHost, p, To);
 986                    var qs = "OptOut/UnSubscribe/?enc=" + Util.EncryptForUrl("{0}|{1}".Fmt(To.PeopleId, From.Address));
 987                    var url = Util.URLCombine(CmsHost, qs);
 988                    var link = @"<a href=""{0}"">Unsubscribe</a>".Fmt(url);
 989                    text = text.Replace("{unsubscribe}", link);
 990                    text = text.Replace("{Unsubscribe}", link);
 991                    if (aa.Count > 0)
 992                    {
 993                        text = text.Replace("{toemail}", aa[0].Address);
 994                        text = text.Replace("%7Btoemail%7D", aa[0].Address);
 995                    }
 996                    text = text.Replace("{fromemail}", From.Address);
 997                    text = text.Replace("%7Bfromemail%7D", From.Address);
 998
 999                    if (Setting("sendemail", "true") != "false")
1000                    {
1001                        Util.SendMsg(sysFromEmail, CmsHost, From,
1002                            emailqueue.Subject, text, aa, emailqueue.Id, To.PeopleId);
1003                        To.Sent = DateTime.Now;
1004
1005                        SubmitChanges();
1006                    }
1007                }
1008                catch (Exception ex)
1009                {
1010                    Util.SendMsg(sysFromEmail, CmsHost, From,
1011                        "sent emails - error: {0}".Fmt(CmsHost), ex.Message,
1012                        Util.ToMailAddressList(From),
1013                        emailqueue.Id, null);
1014                    Util.SendMsg(sysFromEmail, CmsHost, From,
1015                        "sent emails - error: {0}".Fmt(CmsHost), ex.Message,
1016                        Util.SendErrorsTo(),
1017                        emailqueue.Id, null);
1018                }
1019            }
1020            emailqueue.Sent = DateTime.Now;
1021            if (emailqueue.Redacted ?? false)
1022                emailqueue.Body = "redacted";
1023            else if (emailqueue.Transactional == false)
1024            {
1025                var nitems = emailqueue.EmailQueueTos.Count();
1026                if (nitems > 1)
1027                    NotifySentEmails(CmsHost, From.Address, From.DisplayName,
1028                        emailqueue.Subject, nitems, emailqueue.Id);
1029            }
1030            SubmitChanges();
1031        }
1032
1033        private void NotifySentEmails(string CmsHost, string From, string FromName, string subject, int count, int id)
1034        {
1035            if (Setting("sendemail", "true") != "false")
1036            {
1037                var from = new MailAddress(From, FromName);
1038                string subj = "sent emails: " + subject;
1039                var uri = new Uri(new Uri(CmsHost), "/Manage/Emails/Details/" + id);
1040                string body = @"<a href=""{0}"">{1} emails sent</a>".Fmt(uri, count);
1041                var SysFromEmail = Setting("SysFromEmail", ConfigurationManager.AppSettings["sysfromemail"]);
1042                var SendErrorsTo = ConfigurationManager.AppSettings["senderrorsto"];
1043                SendErrorsTo = SendErrorsTo.Replace(';', ',');
1044
1045                Util.SendMsg(SysFromEmail, CmsHost, from,
1046                    subj, body, Util.ToMailAddressList(from), id, null);
1047                var host = uri.Host;
1048                Util.SendMsg(SysFromEmail, CmsHost, from,
1049                    host + " " + subj, body,
1050                    Util.SendErrorsTo(), id, null);
1051            }
1052        }
1053    }
1054}