0

In our company we have confidential information stored in SharePoint. Some politically effective people are pushing to have Alerts sent to their external email addresses. How can I send custom email Alerts with content to internal emails and Alerts without content to external email addresses?

  • Not to put too blunt of a point on this... but why not just have those people that need alerts routed to external addresses simply create a forwarding rule in Outlook based on some aspect of the message, like subject or content? – Dave Wise Aug 29 '13 at 21:58
  • That is a great solution. Forgive me for not including that in my question, but a company policy expressly forbids all auto-forwarding to email addresses outside the company. I knocked on the door and was not granted the exception. – ghangas Aug 30 '13 at 12:55
2

Create new Alert Templates

1 Create a copy of the out of the box templates, change the name, the id, and add the call to the custom handler to each one.

  • Make the custom templates have a “.Ext” at the end of their name.

  • The custom features also call a custom NotificationHandlerAssembly

    using System;
    using System.Runtime.InteropServices;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    using System.Xml;
    
    namespace Prov.Shared.CCPersonalEmail.Features.AlertTemplates
    {
        public class AlertTemplatesEventReceiver : SPFeatureReceiver
        {
            public override void FeatureActivated(SPFeatureReceiverProperties properties)
            {
                //The Properties we will add to the Alert Template
                //(Since this feature lives in the same project with the 
                //IAlertNotifyHandler we just refer to our own assembly)
                string ClassToAdd = "CCPersonalClass";
                SPFeatureDefinition FeatDef = (SPFeatureDefinition)properties.Feature.Definition;
                string ourAssemblyName = FeatDef.ReceiverAssembly;
    
                SPWebApplication WebApplication = (SPWebApplication)properties.Feature.Parent;
                SPWebService WebService = WebApplication.WebService; 
                SPAlertTemplateCollection colSPAT = new SPAlertTemplateCollection(WebService);
    
                foreach (SPAlertTemplate SPAT in colSPAT)
                {
                    if (SPAT.Name.Contains(".Ext"))
                    {
                        SPAT.Delete();
                    }
                }
    
                foreach (SPAlertTemplate SPAT in colSPAT)
                {
                    if (SPAT.Name.Contains("SPAlertTemplateType"))
                    {
                        SPAlertTemplate newSPAT = new SPAlertTemplate();
                        XmlDocument xmlSpit = new XmlDocument();
    
                        xmlSpit.LoadXml(SPAT.Xml.ToString());
    
                        //create node and add value
                        XmlNode node = xmlSpit.CreateNode(XmlNodeType.Element, "Properties", string.Empty);
    
                        //create 3 nodes
                        XmlNode nodeAssembly = xmlSpit.CreateElement("NotificationHandlerAssembly");
                        XmlNode nodeClassName = xmlSpit.CreateElement("NotificationHandlerClassName");
                        XmlNode nodeProperties = xmlSpit.CreateElement("NotificationHandlerProperties");
    
                        nodeAssembly.InnerText = ourAssemblyName;
                        nodeClassName.InnerText = ourAssemblyName.Substring(0,ourAssemblyName.IndexOf(",")) + "." + ClassToAdd;
    
                        //add to parent node
                        node.AppendChild(nodeAssembly);
                        node.AppendChild(nodeClassName);
                        node.AppendChild(nodeProperties);
    
                        //add to elements collection
                        xmlSpit.DocumentElement.AppendChild(node);
    
                        newSPAT.Xml = xmlSpit.InnerXml;
                        newSPAT.Name = SPAT.Name.ToString() + ".Ext";
                        newSPAT.Id = Guid.NewGuid();
    
                        colSPAT.Add(newSPAT);
                    }
                }
    
    
    
                foreach (SPTimerServiceInstance timerservice in WebApplication.Farm.TimerService.Instances)
                {
                    timerservice.Stop();
                    timerservice.Start();
                }
    
            }
    
    
    
            public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
            {
                SPWebApplication WebApplication = (SPWebApplication)properties.Feature.Parent;
                SPWebService WebService = WebApplication.WebService; 
                SPAlertTemplateCollection colSPAT = new SPAlertTemplateCollection(WebService);
    
                foreach (SPAlertTemplate SPAT in colSPAT)
                {
                    if (SPAT.Name.Contains(".Ext"))
                    {
                        SPAT.Delete();
                    }
                }
            }
    
        }
    }
    

Custom Notification Handler assembly

1 The class CCPersonalClass implements the IAlertNotifyHandler interface which contains the defined method OnNotification

  • Attempts to send the custom alert (if it fails send normal alert.)

  • Custom alert:

    • Gets the PersonalEmail field data from the User Information List

    • If Personal Email is empty it just sends normal alert

    • If personal email is populated

      • Send normal email to internal email

      • Send reduced email to external address

      • Replace http:// with http://ext-

      • If sending external email remove all detail records and just leave headers.

{

    using System;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Utilities;
    using System.Text.RegularExpressions;
    using Framework.Logger;
    using System.Collections.Specialized;

    namespace Shared.CCPersonalEmail
    {
        class CCPersonalClass: IAlertNotifyHandler
        {
            public bool OnNotification(SPAlertHandlerParams alertHandler)
            {
                using (new SPMonitoredScope("Shared.CCPersonalClass OnNotification"))
                {
                    string siteUrl = alertHandler.siteUrl;
                    using (SPSite site = new SPSite(siteUrl + alertHandler.webUrl))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {
                            try
                            {
                                return CustomAlertNotification(web, alertHandler, siteUrl);
                            }
                            catch
                            {
                                //if it fails due to configuration still the default alert should work
                                SPUtility.SendEmail(web, alertHandler.headers, alertHandler.body);
                                return false;
                            }
                        }
                    }
                }
            }
            private bool CustomAlertNotification(SPWeb web, SPAlertHandlerParams alertHandler, string siteUrl)
            {
                using (new SPMonitoredScope("Shared.CCPersonalClass CustomAlertNotification"))
                {
                    string PersonalEmail = string.Empty;
                    int eventCount = alertHandler.eventData.GetLength(0);

                    //Get the Personal Email from the User Information List
                    try
                    {
                        PersonalEmail = GetPersonalEmail(alertHandler.headers["to"], web);
                    }
                    catch (Exception e)
                    {
                        Logger.LogError(e, "CCPersonalClass");
                    }

                    //Send each alert in the alertHandler
                    for (int i = 0; i < eventCount; i++)
                    {
                        try
                        {
                            if (string.IsNullOrEmpty(PersonalEmail))
                            {
                                SPUtility.SendEmail(web, alertHandler.headers, alertHandler.body);
                            }
                            else
                            {
                                //Send a normal notification to the work email
                                SPUtility.SendEmail(web, alertHandler.headers, alertHandler.body);

                                //Send an abbreviated email to the external email
                                string txt = alertHandler.body.ToString().Replace("http://", "http://ext-");

                                //Digest messages- removing detail records
                                string strRegex = "<tr>\\s*<td class=\"[a-z]*?\"> <div class=\"nobr\">.*?</td>{0}s*</tr>|";
                                //Immediate messages- removing detail records
                                strRegex = strRegex + "<tr>\\s*<td class=\"formlabel\">.*<td class=\"altvb\">&nbsp;</td>\\s*</tr>";

                                txt = Regex.Replace(txt, strRegex, string.Empty, RegexOptions.Singleline);
                                StringDictionary PersonalHeader = alertHandler.headers;
                                PersonalHeader["cc"] = PersonalEmail;
                                SPUtility.SendEmail(web, PersonalHeader, txt);
                            }
                        }
                        catch (Exception e)
                        {
                           Logger.LogError(e, "CCPersonalClass");
                        }

                    }
                    if (eventCount > 1)
                        return true;
                    else
                        return false;
                }
            }
            private string GetPersonalEmail(string WorkEmail, SPWeb web)
            {
                using (new SPMonitoredScope("Shared.CCPersonalClass GetPersonalEmail"))
                {
                    SPQuery SelectUser = new SPQuery();
                    string SelectUserQS = string.Format("<Query><Where><Eq><FieldRef Name='EMail' /><Value Type='Text'>{0}</Value></Eq></Where></Query>", WorkEmail);
                    SelectUser.Query = SelectUserQS;
                    SPList UserList = web.Site.RootWeb.Lists["User Information List"];
                    SPListItemCollection SelectedUserCol = UserList.GetItems(SelectUser);
                    SPListItem SelectedUser = SelectedUserCol[0];
                    return (string)SelectedUser["PersonalEmail"];
                }
            }
        }
    }

Set the alerts to use the external template

1 Existing content is updated with Feature event receiver:

  • Existing alerts:

    • Site.allwebs.alerts get flipped to the same template name they had with .ext appended
  • Existing Lists:

    • Site.allwebs.lists flipped the attached alerttemplates to their .ext counterpart

{

    using System;
    using System.Runtime.InteropServices;
    using System.Security.Permissions;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Security;
    using Prov.Framework.Logger;

    namespace Prov.Shared.CCPersonalEmail.Features.CCAlerts
    {
        [Guid("a3889d39-ea91-441e-9039-157c512441e4")]
        public class CCAlertsEventReceiver : SPFeatureReceiver
        {
            // Set AlertTemplates on sites and alerts to custom templates.
            public override void FeatureActivated(SPFeatureReceiverProperties properties)
            {
                using (SPSite site = (SPSite)properties.Feature.Parent)
                {
                    AlertTemplateSelector objSelectedAlertTemplate = new AlertTemplateSelector(site);

                    //Add PersonalEmail field if it does not exist yet
                    SPFieldCollection UserInfoFields = site.RootWeb.Lists["User Information List"].Fields;
                    try
                    {
                        bool FieldExists = UserInfoFields.ContainsField("PersonalEmail");
                        if (!FieldExists)
                        {
                            UserInfoFields.Add("PersonalEmail", SPFieldType.Text, false);
                        }
                    }
                    catch (Exception e)
                    {
                        Logger.LogError(e, "CCPersonalClass");
                    }

                    foreach (SPWeb web in site.AllWebs)
                    {
                        using (web)
                        {
                            web.AllowUnsafeUpdates = true;

                            foreach (SPList List in web.Lists)
                            {
                                try
                                {
                                    List.AlertTemplate = objSelectedAlertTemplate.SwapAlertTemplate(List.AlertTemplate.Name, true);
                                    List.Update();
                                }
                                catch (Exception e)
                                {
                                    Logger.LogError(e, "CCAlertsEventReceiver");
                                }
                            }

                            foreach (SPAlert Alert in web.Alerts)
                            {
                                try
                                {
                                    Alert.AlertTemplate = objSelectedAlertTemplate.SwapAlertTemplate(Alert.AlertTemplate.Name, true);
                                    Alert.Update();
                                }
                                catch (Exception e)
                                {
                                    Logger.LogError(e, "CCAlertsEventReceiver");
                                }
                            }
                            web.AllowUnsafeUpdates = false;
                        }
                    }
                }
            }


            // Revert AlertsTemplates on sites and alerts back to out of box templates.

            public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
            {
                using (SPSite site = (SPSite)properties.Feature.Parent)
                {
                    AlertTemplateSelector objSelectedAlertTemplate = new AlertTemplateSelector(site);

                    foreach (SPWeb web in site.AllWebs)
                    {
                        using (web)
                        {
                            web.AllowUnsafeUpdates = true;

                            foreach (SPList List in web.Lists)
                            {
                                try
                                {
                                    List.AlertTemplate = objSelectedAlertTemplate.SwapAlertTemplate(List.AlertTemplate.Name, false);
                                    List.Update();
                                }
                                catch (Exception e)
                                {
                                    Logger.LogError(e, "CCAlertsEventReceiver");
                                }
                            }

                            foreach (SPAlert Alert in web.Alerts)
                            {
                                try
                                {
                                    Alert.AlertTemplate = objSelectedAlertTemplate.SwapAlertTemplate(Alert.AlertTemplate.Name, false);
                                Alert.Update();
                                }
                                catch (Exception e)
                                {
                                    Logger.LogError(e, "CCAlertsEventReceiver");
                                }
                            }
                            web.AllowUnsafeUpdates = false;
                        }
                    }
                }
            }
        }
    }

The following code is used by feature activate, deactivate and the OnListCreation event handler

{

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.SharePoint.Administration;
    using Microsoft.SharePoint;

    namespace Shared.CCPersonalEmail
    {
        class AlertTemplateSelector
        {
            private SPAlertTemplateCollection atc;
            private SPSite site;

            public AlertTemplateSelector(SPSite site)
            {
                this.site = site;
                this.atc = new SPAlertTemplateCollection((SPWebService)site.WebApplication.Parent);
            }

            public SPAlertTemplate SwapAlertTemplate(string PreviousAlert, bool isActivating)
            {

                if ((!PreviousAlert.EndsWith(".Ext")) && (isActivating))
                {
                    PreviousAlert = string.Format("{0}.Ext", PreviousAlert);
                }
                if ((PreviousAlert.EndsWith(".Ext")) && (!isActivating))
                {
                    PreviousAlert = PreviousAlert.Replace(".Ext", "");
                }

                SPAlertTemplate newTemplate = atc[PreviousAlert];
                if (newTemplate == null)
                {
                    if (isActivating)
                    {
                        newTemplate = atc["SPAlertTemplateType.GenericList.Ext"];
                    }
                    else
                    {
                        newTemplate = atc["SPAlertTemplateType.GenericList"];
                    }
                }
                return newTemplate;
            }

        }
    }

2 Future webs and their Future lists

  • SPListEventReceiver ListAdded() event flips the template on all lists created in the site collection

{

    namespace Shared.CCPersonalEmail.OnListCreation
    {
        public class OnListCreation : SPListEventReceiver
        {
           public override void ListAdded(SPListEventProperties properties)
           {
                base.ListAdded(properties);
                SPWeb web = properties.Web;
                SPList List = web.Lists[properties.ListId];

                AlertTemplateSelector objSelectedAlertTemplate = new AlertTemplateSelector(web.Site);
                List.AlertTemplate = objSelectedAlertTemplate.SwapAlertTemplate(List.AlertTemplate.Name, true);
                List.Update();
           }
        }
    }

Housekeeping

1 During feature activation

  • create a PersonalEmail field on the rootweb’s User Information List

  • Code is included in the feature activated event receiver above

2 During feature deactivation

  • PersonalEmail becomes content so it is not removed upon deactivation
0

If it's isolated to just a single list, you might be able to use a workflow instead. You could create a holding list to track the type of thing to email on and a semicolon delimited list of email addresses. When an item is created or edited (whatever your alert logic should be) you look up the appropriate list of email addresses, and email that group the sanitized email.

  • The pilot program was a single web. I recommended to the site owner that they us this route. It does not scale well though. – ghangas Aug 30 '13 at 12:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.