Tuesday 3 August 2021

Sitecore Forms - Triggered Send Email from Marketing Cloud

Hello Sitecore Folks,

Recently, we had a requirement to Trigger automatic emails from Marketing Cloud using Triggered Send data extensions when a Sitecore gets submitted.

Below code is used to send Triggered Email to the email address specified by the end user submitting a form. It can be News Letter Subscription/Contact Us form.

We first need to get the Authorization Token to access the Trigger send Data Extension to Trigger Email. 

We setup the connection using Rest API URL and by providing Client Id and Client Secret. In Response we get the Access Token.


        private string TokenAuthorization()
        {
            Log.Info($"{_salesForceContactModel.EmailAddress} - Authorization Token Call Started", this);
            try
            {
                System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls;

                var client = new HttpClient();

                string clientId = !string.IsNullOrEmpty(ClientId) ? ClientId : DefaultClientId;
                string clientSecret = !string.IsNullOrEmpty(ClientSecret) ? ClientSecret : DefaultClientSecret;

                var dictionaryParam = new Dictionary<string, string>()
                {
                  { "grant_type", "client_credentials" }, { "client_id", clientId}, {"client_secret", clientSecret }
                };

                var content = new FormUrlEncodedContent(dictionaryParam);

                string tokenUri = // TOKEN URL to get the Access Token;

                var response = client.PostAsync(tokenUri, content);

                if (response.Result.IsSuccessStatusCode)
                {
                    var tokenResponse = response.Result.Content.ReadAsStringAsync()?.Result;
                    if (!string.IsNullOrEmpty(tokenResponse))
                    {
                        Log.Info($"{_salesForceContactModel.EmailAddress} - Success : Authorization Token for Trigger Welcome Email from SFMC", this);
                        var objResult = JsonConvert.DeserializeObject<Token>(tokenResponse);
                        return objResult?.access_token;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error($"{_salesForceContactModel.EmailAddress} - Failure : Authorization Token Error from SFMC {ex.Message}", this);
            }
            return null;
        }

 

Once we have the Token Authorization, we can make a call to Triggered Send data extension and pass the Subscriber/User data to trigger emails.

 

private void CallTriggeredSendDefinition()
        {
            try
            {
                Log.Info($"{_salesForceContactModel.EmailAddress} - Starting Trigger Welcome Email from SFMC", this);

                var accessToken = TokenAuthorization();

                if (!string.IsNullOrEmpty(accessToken))
                {
                    var data = GetSubscriberData();

                    if (data != null)
                    {
                        var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");

                        Log.Info($"{_salesForceContactModel.EmailAddress} - Success : Subscriber Data Call", this);

                        var TriggerName = // Email Trigger Name (External API Key on Marketing Cloud Email Trigger)

                        string baseUri = // REST API URL
                        HttpClient client = new HttpClient
                        {
                            BaseAddress = new Uri(string.Format(@"{0}/key:{1}/send", baseUri, currentTriggerName)),
                            DefaultRequestHeaders = { Authorization = new AuthenticationHeaderValue("Bearer", accessToken) }
                        };

                        var response = client.PostAsync(client.BaseAddress, content);

                        if (response.Result.IsSuccessStatusCode)
                        {
                            Log.Info($"{_salesForceContactModel.EmailAddress} - Ending Trigger Welcome Email SFMC using : {currentTriggerName}", this);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("Trigger Send Email exception: " + ex.Message, ex, this);
            }
        }
 

        private object GetSubscriberData()
        {
            SubscriberAttributes sb = new SubscriberAttributes
            {
                sfid = _salesForceContactModel.EmailAddress,
                SubscriberKey = _salesForceContactModel.EmailAddress,
                EmailAddress = _salesForceContactModel.EmailAddress,
                first_name = _salesForceContactModel.FirstName,
                last_name = _salesForceContactModel.LastName
            };
            ContactAttributes ct = new ContactAttributes
            {
                SubscriberAttributes = sb
            };
            To toData = new To
            {
                Address = _salesForceContactModel.EmailAddress,
                SubscriberKey = _salesForceContactModel.EmailAddress,
                ContactAttributes = ct
            };
            PostData data = new PostData
            {
                To = toData
            };

            return data;
        }

 

Below are the classes to access the JSON data.

public class Token
 {
        public string access_token { get; set; }   
 }

 public class SubscriberAttributes
    {
        public string SubscriberKey { get; set; }
        public string EmailAddress { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
    }

    public class ContactAttributes
    {
        public SubscriberAttributes SubscriberAttributes { get; set; }
    }

    public class To
    {
        public string Address { get; set; }
        public string SubscriberKey { get; set; }
        public ContactAttributes ContactAttributes { get; set; }
    }

    public class PostData
    {
        public To To { get; set; }
    }

Thanks !!!

Sitecore - Custom Language Resolver

Hello Sitecore Folks,

If we want to resolve our site with the default context language even though it's not explicitly specified in the URL, then you have arrived at the right place.

e.g. www.site.com or www.site.com/ both the URLs would redirect to www. site.com/en, en is your context language.  

The custom pipeline code will set the context language.


Custom Language Resolver Pipeline:-


public class ContextLanguageResolver : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            try
            {
                StartProfilingOperation("Resolve Context Language and strip trailing Slash", args);

                if (Context.Database == null
                    || Context.Database.Name == "core"
                    || !Context.PageMode.IsNormal
                    || Context.Item == null || Context.Language == null)
                    return;

                // Get out if this item if it isn't in the context site
                if (!Context.Item.Paths.FullPath.StartsWith(Context.Site.StartPath, StringComparison.OrdinalIgnoreCase))
                    return;

                string path = args.HttpContext.Request.RawUrl;
                if (!path.StartsWith($"/{Context.Language.Name.ToLower()}"))
                {
                    Log.Info($"[{this.GetType().Name}.{nameof(Process)}] Referrer URL of the Current Request is `{HttpContext.Current.Request.UrlReferrer}`", this);
                    string url = GetCanonicalUrlWithQuerystring(path);
                    args.HttpContext.Response.Redirect(url);
                }
                else if (path.EndsWith("/"))
                {
                    Log.Info($"[{this.GetType().Name}.{nameof(Process)}] Referrer URL of the Current Request is `{HttpContext.Current.Request.UrlReferrer}`", this);
                    string url = GetCanonicalUrlWithQuerystring(path);
                    args.HttpContext.Response.RedirectPermanent(url);
                }
            }
            finally
            {
                EndProfilingOperation(null, args);
            }
        }

        private static string GetCanonicalUrlWithQuerystring(string path)
        {
            var query = path.IndexOf('?');
            return $"{LinkManager.GetItemUrl(Context.Item)}{(query == -1 ? string.Empty : path.Substring(query))}";
        }
    }

Configuration Patch:-


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:env="http://www.sitecore.net/xmlconfig/env/">
    <sitecore>
        <pipelines>
            <httpRequestBegin>
                <processor type=ProjectName.Website.Pipelines.HttpRequestBegin.ContextLanguageResolver,
ProjectName.Website" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']">
                </processor>
            </httpRequestBegin>
        </pipelines>
    </sitecore>
</configuration>