Implementing newsletter subscriptions

When using the Xperience email marketing features, you manage and send newsletters from the administration interface of the Xperience application. All settings related to email marketing that you configure in Xperience apply (both global settings and the configuration of individual newsletters). On the side of the MVC application, you need to handle the actions that visitors perform on the pages of the website – newsletter subscription and unsubscription.

To allow visitors on your website to manage subscriptions for your newsletters:

  1. Initialize subscription-related services.

  2. Implement the required functionality on your MVC site:

Initializing subscription services

The API for managing newsletter subscriptions is provided by services from the CMS.Newsletters namespace (part of the API provided by the Kentico.Xperience.Libraries NuGet package).

  • ISubscriptionService – handles subscription and unsubscription for email feeds.
  • IUnsubscriptionProvider – allows verification and management of email feed unsubscriptions and opt-out lists.
  • IContactProvider – creates or gets contact objects, which are used to represent newsletter recipients (subscribers).
  • IEmailHashValidator – handles validation of security hashes for email addresses (for unsubscription and subscription confirmation links).
  • ISubscriptionApprovalService – handles approval of subscriptions for newsletters that use double opt-in.

You need to initialize instances of these services and make them available in the related controllers.

We recommend using a dependency injection container to initialize service instances.




using CMS.Newsletters;
using CMS.Core;






        private readonly ISubscriptionService subscriptionService;
        private readonly IUnsubscriptionProvider unsubscriptionProvider;
        private readonly IContactProvider contactProvider;
        private readonly IEmailHashValidator emailHashValidator;
        private readonly ISubscriptionApprovalService subscriptionApprovalService;
        private readonly IIssueInfoProvider issueInfoProvider;
        private readonly INewsletterInfoProvider newsletterInfoProvider;
        private readonly ISiteService siteService;

        public NewsletterSubscriptionController(ISubscriptionService subscriptionService,
                                                IUnsubscriptionProvider unsubscriptionProvider,
                                                IContactProvider contactProvider,
                                                IEmailHashValidator emailHashValidator,
                                                ISubscriptionApprovalService subscriptionApprovalService,
                                                IIssueInfoProvider issueInfoProvider,
                                                INewsletterInfoProvider newsletterInfoProvider,
                                                ISiteService siteService)
        {
            this.subscriptionService = subscriptionService;
            this.unsubscriptionProvider = unsubscriptionProvider;
            this.contactProvider = contactProvider;
            this.emailHashValidator = emailHashValidator;
            this.subscriptionApprovalService = subscriptionApprovalService;
            this.issueInfoProvider = issueInfoProvider;
            this.newsletterInfoProvider = newsletterInfoProvider;
            this.siteService = siteService;
        }



You can now call the methods of the subscription services within your application’s code to perform actions related to newsletter subscription and unsubscription.

Setting up subscription

Use the following approach to develop actions that allow visitors to subscribe to newsletters on your website:

Tip: To view the full code of a functional example, you can inspect and download the LearningKit project on GitHub. You can also run the LearningKit website by connecting the project to an Xperience database.

  1. Create a newsletter for your site in Xperience (or use an existing one). See Creating newsletters for more information.

  2. Create a new controller class in your MVC project or edit an existing one.

  3. Implement two subscription actions – one basic GET action to display a subscription form and a second POST action to handle the creation of new recipients when the form is submitted.

  4. Perform the following steps within the POST action:

    1. Get a ContactInfo object representing the new recipient based on the email address posted from the subscription form – call the GetContactForSubscribing method of the IContactProvider service instance.

    2. Use the INewsletterInfoProvider service to get a NewsletterInfo object representing the newsletter for which you are implementing subscription.

    3. Call the Subscribe method of the ISubscriptionService instance with the prepared ContactInfo and NewsletterInfo objects as parameters.

      Notes:

      • The GetContactForSubscribing method automatically creates the contact object in Xperience based on the specified email address (if necessary).
      • The Subscribe method adds the contact as a recipient of the newsletter.
      • If you wish to check whether a contact with the given email address is already a recipient of a newsletter, call the IsSubscribed method of the ISubscriptionService instance.
      
      
      
       using System;
       using System.Web.Mvc;
      
       using CMS.ContactManagement;
       using CMS.Newsletters;
       using CMS.SiteProvider;
       using CMS.Helpers;
       using CMS.Base;
      
      
      
       
      Subscription actions example
      
      
      
               /// <summary>
               /// Basic action that displays the newsletter subscription form.
               /// </summary>
               public ActionResult Subscribe()
               {
                   return View();
               }
      
               /// <summary>
               /// Handles creation of new marketing email recipients when the subscription form is submitted.
               /// Accepts an email address parameter posted from the subscription form.
               /// </summary>
               [HttpPost]
               [ValidateAntiForgeryToken]
               public ActionResult Subscribe(NewsletterSubscribeViewModel model)
               {
                   if (!ModelState.IsValid)
                   {
                       // If the subscription data is not valid, displays the subscription form with error messages
                       return View(model);
                   }
      
                   // Either gets an existing contact by email or creates a new contact object with the given email
                   ContactInfo contact = contactProvider.GetContactForSubscribing(model.Email);
      
                   // Gets a newsletter
                   // Fill in the code name of your newsletter object in Xperience
                   NewsletterInfo newsletter = newsletterInfoProvider.Get("SampleNewsletter", siteService.CurrentSite.SiteID);
      
                   // Prepares settings that configure the subscription behavior
                   var subscriptionSettings = new SubscribeSettings()
                   {
                       RemoveAlsoUnsubscriptionFromAllNewsletters = true, // Subscription removes the given email address from the global opt-out list for all marketing emails (if present)
                       SendConfirmationEmail = true, // Allows sending of confirmation emails for the subscription
                       AllowOptIn = true // Allows handling of double opt-in subscription for newsletters that have it enabled
                   };
      
                   // Subscribes the contact with the specified email address to the given newsletter
                   subscriptionService.Subscribe(contact, newsletter, subscriptionSettings);
      
                   // Passes information to the view, indicating whether the newsletter requires double opt-in for subscription
                   model.RequireDoubleOptIn = newsletter.NewsletterEnableOptIn;
      
                   // Displays a view to inform the user that the subscription was successful
                   return View("SubscribeSuccess", model);
               }
      
      
      
       
  5. We recommend creating a view model for your subscription action (NewsletterSubscribeViewModel in the example above). The view model allows you to:

    • Pass parameters from the subscription form (email address and, optionally, the recipient’s name).

    • Use data annotations to define validation and formatting rules for the subscription data. See System.ComponentModel.DataAnnotations for more information about the available data annotation attributes.

    • Pass information back to the subscription form, for example, a flag indicating that the newsletter requires a double opt-in subscription.

      Subscription view model example
      
      
      
        using System.ComponentModel;
        using System.ComponentModel.DataAnnotations;
      
        public class NewsletterSubscribeViewModel
        {
            [DataType(DataType.EmailAddress)]
            [Required(ErrorMessage = "The Email address cannot be empty.")]
            [DisplayName("Email address")]
            [EmailAddress(ErrorMessage = "Invalid email address.")]
            [MaxLength(254, ErrorMessage = "The Email address cannot be longer than 254 characters.")]
            public string Email
            {
                get;
                set;
            }
      
            /// <summary>
            /// Indicates whether the newsletter requires double-opt in for subscription.
            /// Allows the view to display appropriate information to newly subscribed users.
            /// </summary>
            [Bindable(false)]
            public bool RequireDoubleOptIn
            {
                get;
                set;
            }
        }
      
      
        
  6. Design the user interface required for newsletter subscription on your website:

    • Create a view for the Subscribe action and display an appropriate subscription form. We recommend using a strongly typed view based on your subscription view model.
    • Create any other views required by the Subscribe action, for example, to inform users about successful subscriptions.

Your website’s visitors can now subscribe to newsletters. The system automatically performs all standard Xperience subscription features, such as sending of confirmation emails based on the newsletter’s Subscription confirmation template.

Continue by setting up:

Setting up unsubscription

Users can unsubscribe from email campaigns or newsletters by clicking links placed into the content of your emails.

When setting up unsubscription functionality, you first need to ensure that the Xperience application generates correct unsubscription links:

  1. Add unsubscription links into the content of your marketing emails or templates (in the Email marketing application).

    Use the {% EmailFeed.UnsubscribeFromEmailFeedUrl %} and {% EmailFeed.UnsubscribeFromAllEmailFeedsUrl %} macros to generate the URLs of the links.

    See Preparing email templates to learn more.

  2. In the Email marketing application, edit your email feeds, open their Configuration tab and set the following properties:

    • Base URL – the base URL of your live site. Leave empty to use the site’s Presentation URL. For example: https://www.SiteDomain.com

    • Unsubscription page URL – must match the route of the action that handles unsubscription requests in your MVC application. For example: ~/NewsletterSubscription/Unsubscribe

      To use a shared Unsubscription page URL for multiple email feeds, leave the property empty for individual email feeds and set the value in the Settings -> On-line marketing -> Email marketing -> Unsubscription page URL setting.

Continue by implementing a controller action within your MVC application for handling of unsubscription requests:

  1. Create a new model class in your MVC project. The model will represent the parameters that Xperience generates in newsletter and email campaign unsubscription links.

    Unsubscription model
    
    
    
     using System;
     using System.ComponentModel.DataAnnotations;
    
     public class MarketingEmailUnsubscribeModel
     {
         /// <summary>
         /// The email address of the recipient who is requesting unsubscription.
         /// </summary>
         [Required]
         public string Email
         {
             get;
             set;
         }
    
         /// <summary>
         /// The GUID (identifier) of the Xperience email feed related to the unsubscription request.
         /// </summary>
         [Required]
         public Guid NewsletterGuid
         {
             get;
             set;
         }
    
         /// <summary>
         /// The GUID (identifier) of the Xperience marketing email related to the unsubscription request.
         /// </summary>
         [Required]
         public Guid IssueGuid
         {
             get;
             set;
         }
    
         /// <summary>
         /// Hash for protection against forged unsubscription requests.
         /// </summary>
         [Required]
         public string Hash
         {
             get;
             set;
         }
    
         /// <summary>
         /// Indicates whether the unsubscription request is for all marketing emails or only a specific email feed.
         /// </summary>
         public bool UnsubscribeFromAll
         {
             get;
             set;
         }
     }
    
    
     
  2. Add a GET action to your newsletter subscription controller and perform the following steps:

    1. Use the unsubscription model to pass parameters from the unsubscription request.

    2. Verify that the unsubscription hash is valid for the given email address – call the ValidateEmailHash method of the IEmailHashValidator service instance, with the email address and hash from the unsubscription request as parameters.

    3. Verify that the email address is not already unsubscribed by calling the IsUnsubscribedFromSingleNewsletter and IsUnsubscribedFromAllNewsletters methods of the IUnsubscriptionProvider service. Branch the logic based on the UnsubscribeFromAll parameter from the unsubscription request.

    4. To remove subscriptions, call the UnsubscribeFromSingleNewsletter or UnsubscribeFromAllNewsletters method of the ISubscriptionService instance (with parameters taken from the unsubscription request).

      
      
      
       using System.Web.Mvc;
      
       using CMS.Newsletters;
       using CMS.SiteProvider;
      
      
       
      Unsubscription action example
      
      
      
               /// <summary>
               /// Handles marketing email unsubscription requests.
               /// </summary>
               public ActionResult Unsubscribe(MarketingEmailUnsubscribeModel model)
               {
                   // Verifies that the unsubscription request contains all required parameters
                   if (ModelState.IsValid)
                   {
                       // Confirms whether the hash in the unsubscription request is valid for the given email address
                       // Provides protection against forged unsubscription requests
                       if (emailHashValidator.ValidateEmailHash(model.Hash, model.Email))
                       {
                           // Gets the marketing email (issue) from which the unsubscription request was sent
                           IssueInfo issue = issueInfoProvider.Get(model.IssueGuid, siteService.CurrentSite.SiteID);
      
                           if (model.UnsubscribeFromAll)
                           {
                               // Checks that the email address is not already unsubscribed from all marketing emails
                               if (!unsubscriptionProvider.IsUnsubscribedFromAllNewsletters(model.Email))
                               {
                                   // Unsubscribes the specified email address from all marketing emails (adds it to the opt-out list)
                                   subscriptionService.UnsubscribeFromAllNewsletters(model.Email, issue?.IssueID);
                               }
                           }
                           else
                           {
                               // Gets the email feed for which the unsubscription was requested
                               NewsletterInfo newsletter = newsletterInfoProvider.Get(model.NewsletterGuid, siteService.CurrentSite.SiteID);
      
                               if (newsletter != null)
                               {
                                   // Checks that the email address is not already unsubscribed from the specified email feed
                                   if (!unsubscriptionProvider.IsUnsubscribedFromSingleNewsletter(model.Email, newsletter.NewsletterID))
                                   {
                                       // Unsubscribes the specified email address from the email feed
                                       subscriptionService.UnsubscribeFromSingleNewsletter(model.Email, newsletter.NewsletterID, issue?.IssueID);
                                   }
                               }
                           }
      
                           // Displays a view to inform the user that they were unsubscribed
                           return View("UnsubscribeSuccess");
                       }
                   }
      
                   // If the unsubscription was not successful, displays a view to inform the user
                   // Failure can occur if the request does not provide all required parameters or if the hash is invalid
                   return View("UnsubscribeFailure");
               }
      
      
      
       
  3. Create views for informing recipients about the result of their unsubscription requests.

When a user clicks on an unsubscription link in a newsletter, the URL leads to the unsubscribe action on your MVC site. The action validates the parameters and either unsubscribes the recipient or displays information if the request is invalid.

The content of the unsubscription page depends on your implementation of the returned views.

Handling confirmations for double opt-in subscription

If your newsletters are configured to use double opt-in, you need to set up additional logic on your MVC site to allow users to confirm their newsletter subscriptions.

When a visitor subscribes to a newsletter with double opt-in enabled, the system automatically adds the recipient with the “Waiting for confirmation” subscription status and the system sends a confirmation email to the given email address. You need to ensure that the double opt-in email contains a correct confirmation link:

  1. Add the confirmation link into the content of the Double opt-in template assigned to your newsletter (in the Email marketing application).

    Use the {% EmailFeed.SubscriptionConfirmationUrl %} macro to generate the URL of the subscription confirmation link. See Preparing email templates to learn more.

  2. In the Email marketing application, edit your newsletters, open their Configuration tab and set the following properties:

    • Base URL – the base URL of your live site. Leave empty to use the site’s Presentation URL. For example: https://www.SiteDomain.com
    • Approval page URL – must match the route of the action that handles subscription confirmation requests in your MVC application. For example: ~/NewsletterSubscription/ConfirmSubscription

To use a shared Approval page URL for multiple newsletters, leave the property empty for individual newsletters and set the value in the Settings -> On-line marketing -> Email marketing -> Double opt-in approval page URL setting.

Continue by implementing a controller action within your MVC application to handle the subscription confirmation requests:

  1. Create a new model class in your MVC project. The model will represent the parameters that Xperience generates in subscription confirmation links.

    Subscription confirmation view model
    
    
    
     using System.ComponentModel.DataAnnotations;
    
     public class NewsletterConfirmSubscriptionViewModel
     {
         /// <summary>
         /// Hash used to identify the subscription and for protection against forged confirmation requests.
         /// </summary>
         [Required]
         public string SubscriptionHash
         {
             get;
             set;
         }
    
         /// <summary>
         /// The date and time of the original subscription. Used to detect expired confirmation requests.
         /// </summary>
         public string DateTime
         {
             get;
             set;
         }
     }
    
    
     
  2. Add a GET action to your newsletter subscription controller and perform the following steps:

    1. Use the subscription confirmation model to pass parameters from the confirmation request.

    2. Parse the date and time parameter from the confirmation request. The value uses a specific date and time format required by the Xperience API (see the code example below for details).

    3. To confirm subscriptions, call the ApproveSubscription method of the ISubscriptionApprovalService instance (with parameters taken from the hash and DateTime value in the confirmation request).

    4. Handle the result of the subscription confirmation based on the ApprovalResult value returned by the ApproveSubscription method (see the code example below for information about the possible values).

      
      
      
       using System;
       using System.Web.Mvc;
      
       using CMS.ContactManagement;
       using CMS.Newsletters;
       using CMS.SiteProvider;
       using CMS.Helpers;
       using CMS.Base;
      
      
      
       
      Subscription confirmation action example
      
      
      
               /// <summary>
               /// Handles confirmation requests for newsletter subscriptions (when using double opt-in).
               /// </summary>
               public ActionResult ConfirmSubscription(NewsletterConfirmSubscriptionViewModel model)
               {
                   // Verifies that the confirmation request contains the required hash parameter
                   if (!ModelState.IsValid)
                   {
                       // If the hash is missing, returns a view informing the user that the subscription confirmation was not successful
                       ModelState.AddModelError(String.Empty, "The confirmation link is invalid.");
                       return View(model);
                   }
      
                   // Attempts to parse the date and time parameter from the request query string
                   // Uses the date and time formats required by the Xperience API
                   DateTime parsedDateTime = DateTimeHelper.ZERO_TIME;
                   if (!string.IsNullOrEmpty(model.DateTime) && !DateTimeUrlFormatter.TryParse(model.DateTime, out parsedDateTime))
                   {
                       // Returns a view informing the user that the subscription confirmation was not successful
                       ModelState.AddModelError(String.Empty, "The confirmation link is invalid.");
                       return View(model);
                   }
      
                   // Attempts to confirm the subscription specified by the request's parameters
                   ApprovalResult result = subscriptionApprovalService.ApproveSubscription(model.SubscriptionHash, false, siteService.CurrentSite.SiteName, parsedDateTime);
      
                   switch (result)
                   {
                       // The confirmation was successful or the recipient was already approved
                       // Displays a view informing the user that the subscription is active
                       case ApprovalResult.Success:
                       case ApprovalResult.AlreadyApproved:
                           return View(model);
      
                       // The confirmation link has expired
                       // Expiration occurs after a certain number of hours from the time of the original subscription
                       // You can set the expiration interval in Xperience (Settings -> On‑line marketing -> Email marketing -> Double opt-in interval)
                       case ApprovalResult.TimeExceeded:
                           ModelState.AddModelError(String.Empty, "Your confirmation link has expired. Please subscribe to the newsletter again.");
                           break;
      
                       // The subscription specified in the request's parameters does not exist
                       case ApprovalResult.NotFound:
                           ModelState.AddModelError(String.Empty, "The subscription that you are attempting to confirm does not exist.");
                           break;
      
                       // The confirmation failed
                       default:
                           ModelState.AddModelError(String.Empty, "The confirmation of your newsletter subscription did not succeed.");
                           break;
                   }           
      
                   // If the subscription confirmation was not successful, displays a view informing the user
                   return View(model);
               }
      
      
      
       
  3. Create a view for informing users about the result of their subscription confirmation requests.

When a new recipient clicks the link in the confirmation email received after subscribing to a newsletter with double opt-in enabled, the confirmation action on your MVC site attempts to confirm the subscription. If the link is valid (based on the expiration interval configured in the Xperience application in Settings -> On‑line marketing -> Email marketing -> Double opt-in interval), the system updates the recipient to the “Subscribed” status and the newsletter subscription becomes active.

The content of the subscription approval page depends on your implementation of the returned views.

Validating email feed URLs

The Email marketing application in the Xperience administration interface automatically checks the accessibility of the URLs specified for each email feed. This includes the following:

If any of the given URLs are not accessible, the system displays a warning on the Emails tab of the email feed and the Send tab of individual emails.