Sitecore 9 Forms Part-3 – Send EXM E-mail Submit Action

Hello Sitecorians,

EXM can only send e-mails to a known contact. Sending the e-mails to the current user, should be registered as a contact in Sitecore. We can’t send the EXM e-mail while submitting the form.For this required to update the contact .In this custom action while submitting the form we will just update the contact and also send the EXM e-mail.

Sitecore 9 Forms introduced the few submit actions.Also there is one “Send Email Campaign Message” action .We can send the email to only predefined fixed  contacts from this action.

How to Solve this ?

To solve this problem we have to create the custom submit action.What are the things need to do for this activity

  1. Create the speak editor control for SendEmail action
  2. Create custom submit action class
  3. Create the client script for the editor
  4. Create the SendEmail action item.

Create the speak editor control for SendEmail action

We require Sitecore rocks Visual studio extension to create this fast.Please follow the below steps,

For Sitecore Forms, submit action editors are located in the core database:/sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions

We need the below fields in this editor

  1. Firstname mapping field
  2. Lastname mapping field
  3. Email mapping field(required)
  4. Message selection field

To create the control:

  1. Navigate to /sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions.
  2. Right-click Actions, click Add, and then click New Item.

New Base

3. Select the /sitecore/client/Speak/Templates/Pages/Speak-BasePage template, and in the Enter the name of the new item field, enter SendEmail and click ok

speak base page

4. Right-click the SendEmail item you just created and click Tasks, and click Design Layout.

Design layout.jpg

5. In the Layout dialog box, navigate to /sitecore/client/Speak/Layouts/Layouts and select the Speak-FlexLayout layout and click OK.

Layout selection

6. In the upper-left corner, click Add Rendering and in the Select Renderings dialog box, click All and search for PageCode:

Pagecode

7. Select PageCode and click OK.

8. In the PageCode rendering properties, set the PageCodeScriptFileName property to the JavaScript path that contains the page code script:

/sitecore/shell/client/Applications/FormsBuilder/Layouts/Actions/SendEmail.js

9. Set the SpeakCoreVersion property to Speak 2-x.

10. Search for and select the Text View rendering and click Add to add three items: HeaderTitleHeaderSubtitle, and ValueNotInListText.

TextView

11. For the three items, in the Properties section, set the following ID properties:

  • IsVisible – False
  • PlaceholderKey – Page.Body

12. Add the following renderings:

  • MainBorder of type Border.
  • MapContactForm of type Form. Set the FieldsLayout property to 1-1-1-1 and set the PlaceholderKey property to MainBorder.Content.

13. Click Add Rendering and in the Select Renderings dialog box, click All and search for GenericDatasource and set Id property as MessageDataSource

Genericdatasource

14. Your final rendering should be look like below.

FinalRendering

Next, you must add a folder that contains the parameters for the editor.

To add the PageSettings folder:

  1. Navigate to /sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions, and right-click the SendEmail item you created earlier, click Add, and click New item.
  2. Search for and select the PageSettings template, enter the name PageSettings and click OK.

Pagesettings

3. Right-click the PageSettings item that you just created and click AddNew Item.

4. Search for and select the Text Parameters template and click Add three times and          name the items exactly the same as in the IDs in the layout you created previously:

  • HeaderTitle – double-click and in the Text field enter: Map form fields to contact details.
  • HeaderSubtitle – double-click and in the Text field enter: Map the fields in the form to the contact details that you want to update.
  • ValueNotInListText – double-click and in the Text field enter: value not in selection list.

Textparameters

5.  Right-click the PageSettings item that you just created and click AddNew Item and Search “GenericDataSource Parameters” and name it as MessagesDataSource.

MessageDatasource.jpg

6. Set the Service Url to “/sitecore/api/exm/campaigns/automated”

ServiceUrl

7. Navigate to /sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions and right-click the PageSettings item that you just created.

8. Click New Folder and name it MapContactForm.

9. Click the MapContactForm folder and add three FormDropList Parameters templates with the following field values:

 

mapping field

10. Click the MapContactForm folder and add the FormDropList Parameters templates and name it Message and set the below values.

  • ValueFieldName – Id
  • DisplayFieldName – Name
  • Form Label – Select email campaign message
  • BindingConfiguration – messageId/Selectedvalue

Message Binding

11. Navigate to the SendEmail layout and set the Form rendering ConfigurationItem property to the ID of the MapContactForm folder that contains the FormDropList parameters.

ConfigureItem

12. Navigate to /sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions and right-click the PageSettings item that you created earlier.

13. Add a new item named Stylesheet of type Page-Stylesheet-File

stylesheet

14. Set the Stylesheet value to “/sitecore/shell/client/Applications/FormsBuilder/Layouts/Actions/Actions.css”

action css

Now our sitecore side work is done.Lets start the coding

To create a submit action class:

  1. Create the SendEmail class and inherit from the SubmitActionBase<TParametersData> class.
  2. Specify the SendEmailData parameter.
  3. Override the Execute method.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.Analytics;
using Sitecore.Analytics.Model;
using Sitecore.DependencyInjection;
using Sitecore.Diagnostics;
using Sitecore.EmailCampaign.Cd.Services;
using Sitecore.EmailCampaign.Model.Messaging;
using Sitecore.ExperienceForms.Models;
using Sitecore.ExperienceForms.Processing;
using Sitecore.ExperienceForms.Processing.Actions;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using Sitecore.XConnect.Client.Configuration;
using Sitecore.XConnect.Collection.Model;

namespace Sitecore.Scientist.Forms.SubmitActions
{

    /// <summary>
    /// Submit action for updating <see cref="PersonalInformation"/> and <see cref="EmailAddressList"/> facets of a <see cref="XConnect.Contact"/>.
    /// </summary>
    /// <seealso cref="Sitecore.ExperienceForms.Processing.Actions.SubmitActionBase{SendEmailData}" />
    public class SendEmail : SubmitActionBase<SendEmailData>
    {
        private readonly IClientApiService _clientApiService;
        /// <summary>
        /// Initializes a new instance of the <see cref="SendEmailData"/> class.
        /// </summary>
        /// <param name="submitActionData">The submit action data.</param>
        public SendEmail(ISubmitActionData submitActionData) : base(submitActionData)
        {
            _clientApiService= ServiceLocator.ServiceProvider.GetService<IClientApiService>();
        }
        
        /// <summary>
        /// Gets the current tracker.
        /// </summary>
        protected virtual ITracker CurrentTracker => Tracker.Current;
        /// <summary>
        /// Executes the action with the specified <paramref name="data" />.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="formSubmitContext">The form submit context.</param>
        /// <returns><c>true</c> if the action is executed correctly; otherwise <c>false</c></returns>
        protected override bool Execute(SendEmailData data, FormSubmitContext formSubmitContext)
        {
            Assert.ArgumentNotNull(data, nameof(data));
            Assert.ArgumentNotNull(formSubmitContext, nameof(formSubmitContext));
            var firstNameField = GetFieldById(data.FirstNameFieldId, formSubmitContext.Fields);
            var lastNameField = GetFieldById(data.LastNameFieldId, formSubmitContext.Fields);
            var emailField = GetFieldById(data.EmailFieldId, formSubmitContext.Fields);
            if (firstNameField == null && lastNameField == null && emailField == null)
            {
                return false;
            }
            using (var client = CreateClient())
            {
                try
                {
                    var source = "Subcribe.Form";
                    var id = CurrentTracker.Contact.ContactId.ToString("N");
                    CurrentTracker.Session.IdentifyAs(source, id);
                    var trackerIdentifier = new IdentifiedContactReference(source, id);
                    var expandOptions = new ContactExpandOptions(
                        CollectionModel.FacetKeys.PersonalInformation,
                        CollectionModel.FacetKeys.EmailAddressList);
                    Contact contact = client.Get(trackerIdentifier, expandOptions);
                    SetPersonalInformation(GetValue(firstNameField), GetValue(lastNameField), contact, client);
                    SetEmail(GetValue(emailField), contact, client);
                    client.Submit();
                    if (data.MessageId == Guid.Empty)
                    {
                        Logger.LogError("Empty message id");
                        return false;
                    }
                    ContactIdentifier contactIdentifier = null;
                    if (contact != null)
                    {
                        contactIdentifier = contact.Identifiers.FirstOrDefault<ContactIdentifier>((ContactIdentifier c) => c.IdentifierType == ContactIdentifierType.Known);
                    }
                    if (contactIdentifier == null)
                    {
                        Logger.LogError("Contact id is null.");
                        return false;
                    }
                    try
                    {
                        _clientApiService.SendAutomatedMessage(new AutomatedMessage()
                        {
                            ContactIdentifier = contactIdentifier,
                            MessageId = data.MessageId
                        });
                    }
                    catch (Exception exception1)
                    {
                        Logger.LogError(exception1.Message, exception1);
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex.Message, ex);
                    return false;
                }
            }
        }
       
        /// <summary>
        /// Creates the client.
        /// </summary>
        /// <returns>The <see cref="IXdbContext"/> instance.</returns>
        protected virtual IXdbContext CreateClient()
        {
            return SitecoreXConnectClientConfiguration.GetClient();
        }
        /// <summary>
        /// Gets the field by <paramref name="id" />.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <param name="fields">The fields.</param>
        /// <returns>The field with the specified <paramref name="id" />.</returns>
        private static IViewModel GetFieldById(Guid id, IList<IViewModel> fields)
        {
            return fields.FirstOrDefault(f => Guid.Parse(f.ItemId) == id);
        }
        /// <summary>
        /// Gets the <paramref name="field" /> value.
        /// </summary>
        /// <param name="field">The field.</param>
        /// <returns>The field value.</returns>
        private static string GetValue(object field)
        {
            return field?.GetType().GetProperty("Value")?.GetValue(field, null)?.ToString() ?? string.Empty;
        }
        /// <summary>
        /// Sets the <see cref="PersonalInformation"/> facet of the specified <paramref name="contact" />.
        /// </summary>
        /// <param name="firstName">The first name.</param>
        /// <param name="lastName">The last name.</param>
        /// <param name="contact">The contact.</param>
        /// <param name="client">The client.</param>
        private static void SetPersonalInformation(string firstName, string lastName, Contact contact, IXdbContext client)
        {
            if (string.IsNullOrEmpty(firstName) && string.IsNullOrEmpty(lastName))
            {
                return;
            }
            PersonalInformation personalInfoFacet = contact.Personal() ?? new PersonalInformation();
            if (personalInfoFacet.FirstName == firstName && personalInfoFacet.LastName == lastName)
            {
                return;
            }
            personalInfoFacet.FirstName = firstName;
            personalInfoFacet.LastName = lastName;
            client.SetPersonal(contact, personalInfoFacet);
        }
        /// <summary>
        /// Sets the <see cref="EmailAddressList"/> facet of the specified <paramref name="contact" />.
        /// </summary>
        /// <param name="email">The email address.</param>
        /// <param name="contact">The contact.</param>
        /// <param name="client">The client.</param>
        private static void SetEmail(string email, Contact contact, IXdbContext client)
        {
            if (string.IsNullOrEmpty(email))
            {
                return;
            }
            EmailAddressList emailFacet = contact.Emails();
            if (emailFacet == null)
            {
                emailFacet = new EmailAddressList(new EmailAddress(email, false), "Preferred");
            }
            else
            {
                if (emailFacet.PreferredEmail?.SmtpAddress == email)
                {
                    return;
                }
                emailFacet.PreferredEmail = new EmailAddress(email, false);
            }
            client.SetEmails(contact, emailFacet);
        }
    }
    public class SendEmailData
    {
        public Guid FirstNameFieldId { get; set; }
        public Guid LastNameFieldId { get; set; }
        public Guid EmailFieldId { get; set; }
        public Guid MessageId { get; set; }
       
    }
}

Now you must create the client script for the editor. In a previous step, when you created the SendEmail item, you set the path to the script as follows:

/sitecore/shell/client/Applications/FormsBuilder/Layouts/Actions/SendEmail.js

To create the script like below:

(function (speak) {
    var parentApp = window.parent.Sitecore.Speak.app.findApplication('EditActionSubAppRenderer'),
     messageParameterName = "messageId";
        designBoardApp = window.parent.Sitecore.Speak.app.findComponent('FormDesignBoard');
    var getFields = function () {
        var fields = designBoardApp.getFieldsData();
        return _.reduce(fields,
            function (memo, item) {
                if (item && item.model && item.model.hasOwnProperty("value")) {
                    memo.push({
                        itemId: item.itemId,
                        name: item.model.name
                    });
                }
                return memo;
            },
            [
                {
                    itemId: '',
                    name: ''
                }
            ],
            this);
    };
    speak.pageCode(["underscore"],
        function (_) {
            return {
                initialized: function () {
                    this.on({
                        "loaded": this.loadDone
                    },
                        this);
                    this.Fields = getFields();
                    this.MapContactForm.children.forEach(function (control) {
                        if (control.deps && control.deps.indexOf("bclSelection") !== -1) {
                            control.IsSelectionRequired = false;
                        }
                    });
                    var componentName = this.Form.bindingConfigObject[messageParameterName].split(".")[0];
                    this.MessagesList = this.MapContactForm[componentName];
                    this.MessagesList.on("change:SelectedItem", this.changedSelectedItemId, this);

                    this.MessagesDataSource.on("change:DynamicData", this.messagesChanged, this);
                   
                    if (parentApp) {
                        parentApp.loadDone(this, this.HeaderTitle.Text, this.HeaderSubtitle.Text);
                        parentApp.setSelectability(this, true);
                    }
                },
                changedSelectedItemId: function () {
                    var isSelectable = this.MessagesList.SelectedValue && this.MessagesList.SelectedValue.length;
                    parentApp.setSelectability(this, isSelectable);
                },
                setDynamicData: function (propKey) {
                    var componentName = this.MapContactForm.bindingConfigObject[propKey].split(".")[0];
                    var component = this.MapContactForm[componentName];
                    var items = this.Fields.slice(0);
                    if (this.Parameters[propKey] &&
                        !_.findWhere(items, { itemId: this.Parameters[propKey] })) {
                        var currentField = {
                            itemId: this.Parameters[propKey],
                            name: this.Parameters[propKey] +
                                " - " +
                                (this.ValueNotInListText.Text || "value not in the selection list")
                        };
                        items.splice(1, 0, currentField);
                        component.DynamicData = items;
                        $(component.el).find('option').eq(1).css("font-style", "italic");
                    } else {
                        component.DynamicData = items;
                    }
                },
                setMessageData: function (listComponent, data, currentValue) {
                    var items = data.slice(0);
                    items.unshift({ Id: "", Name: "" });

                    if (currentValue && !_.findWhere(items, { Id: currentValue })) {
                        items.unshift({
                            Id: "",
                            Name: currentValue +
                                " - " +
                                (this.ValueNotInListText.Text || "value not in the selection list")
                        });

                        listComponent.DynamicData = items;
                        $(listComponent.el).find('option').eq(0).css("font-style", "italic");
                    } else {
                        listComponent.DynamicData = items;
                        listComponent.SelectedValue = currentValue;
                    }
                },
                messagesChanged: function (items) {
                    this.setMessageData(this.MessagesList, items, this.Parameters[messageParameterName]);
                },
                loadDone: function (parameters) {
                    this.Parameters = parameters || {};
                    _.keys(this.MapContactForm.bindingConfigObject).forEach(this.setDynamicData.bind(this));
                    this.MapContactForm.BindingTarget = this.Parameters;
                },
                getData: function () {
                    var formData = this.MapContactForm.getFormData(),
                        keys = _.keys(formData);
                    keys.forEach(function (propKey) {
                        if (formData[propKey] == null || formData[propKey].length === 0) {
                            if (this.Parameters.hasOwnProperty(propKey)) {
                                delete this.Parameters[propKey];
                            }
                        } else {
                            this.Parameters[propKey] = formData[propKey];
                        }
                    }.bind(this));
                    return this.Parameters;
                }
            };
        });
})(Sitecore.Speak);

Update the CSS

You can update your custom CSS in the below path

/sitecore/shell/client/Applications/FormsBuilder/Layouts/Actions/Actions.css

To create the submit action item:

  1. Navigate to /sitecore/system/Settings/Forms/Submit Actions
  2. Right-click Submit Actions, click Insert, and click Insert from template.
  3. Select the /System/Forms/Submit Action template, in the Item Name field, enter the name SendEmail and click Insert.
  4. Navigate to the item you just created and in the Settings section, in the Model Type field, set the value to the class type name.

Email submit action

  1. In the Error Message field, enter an error message, for example, Sending Email failed!
  2. In the Editor field, select the editor that you just created, for example, SendEmail.
  3. In the Appearance section, select the icon that you want to display in the Form elements .

To execute this submit action , we required to create the  following things

  • Automated EXM Email Message
  • Email Subscription form

Create Automated EXM Email Message:

Initially we need to setup the EXM mail server settings.

If you are not configured the SMTP .Please follow the this blog .

automated email

 

I have created the automated email campaign named “welcome” without adding any recipient.

welcome automated

 

welcome automated detail.jpg

Create Email Subscription form

Now one thing is pending .Just create the form and add the newly created SendEmail action to submit button.

Select SendEmail

 

Send email mapping

Select the “Welcome Automated” and click OK.Save the form changes.

Finally we are done.Lets test the functionality.

Just fill the form and submit.

testing exm

For the testing purpose , i have used the yopmail account.Now i am going to open the yopmail inbox.

testing exm mail

I got the Email through EXM . Also it should created the contacts as well. Lets check the contact as well.If its not showing in Experience Profile just wait for 10-15 minutes or close the Session.

contact details

If you check the EXM Campaign message details.It looks like below.

Email activity

giphy

If you are feeling lazy to create the above steps, then just download the sitecore packages for SendEmail submit action from  Sitecore Exm email submit action-1.0.zip.

Download the Source code from Github.

Thank you visiting this blog.Please share your thoughts in comments.

 

Sitecore 9 Forms Part-2 – Google Recaptcha Field

Hello Sitecorians,

I’ve been exploring Sitecore Forms and implemented the Google Re-captcha form element within the Sitecore forms. Re-captcha is the basic requirement for the form submission process and in order to implement this, we’ve to write some code to it.

We will go through step by step process to implement the Google Recaptcha with Sitecore Forms using the custom form element. And will create a generic component which you can use for any other form as well.

How to do ?

First we will create the sitecore template changes,

We will need the following fields for Recapthca Field

  • Public Key
  • Private Key
  • Error Message

Core DB Changes :

1. Go to the “/sitecore/client/Applications/FormsBuilder/Components/Layouts/PropertyGridForm/PageSettings/Common/Details” and duplicate the “FieldName” Item.

2. Update the “FormLabel” to “Private Key” and “BindingConfiguration” like below screenshot

PrivateKey

3. Do the same steps to the remaining two fields.

Now we need to create the Property Editor.

  1. Go to the “/sitecore/client/Applications/FormsBuilder/Components/Layouts/PropertyGridForm/PageSettings/Settings” Item.
  2. Duplicate the “SingleLineText” and Update the details section.
  3. Add the newly created details items to “GoogleRecapthca” item like below screenshot

Recpatcha details

Master DB changes :

  1. Go to the “/sitecore/templates/System/Forms/Fields” and duplicate the “/sitecore/templates/System/Forms/Fields/Input” Item.
  2. We need to add the below fields in the “Google Recaptcha” Item and create the _StandardValues as well.
  • Public Key
  • Private Key
  • Error Message

Google Recaptcha Template

3. Update the PrivateKey and PublicKey in _StandardValues .

Google Recaptcha Standard Value

 Code Changes : 

Create the new class “GoogleRecaptcha” like below,

using Sitecore.Data.Items;
using Sitecore.ExperienceForms.Mvc.Models.Fields;

namespace Sitecore.Scientist.Forms.Fields
{
    public class GoogleRecaptcha : StringInputViewModel
    {
        public string PublicKey { get; set; }

        public string PrivateKey { get; set; }

        public string ErrorMessage { get; set; }

        protected override void InitItemProperties(Item item)
        {
            base.InitItemProperties(item);

            PublicKey = StringUtil.GetString(item.Fields["Public Key"]);
            PrivateKey = StringUtil.GetString(item.Fields["Private Key"]);
            ErrorMessage = StringUtil.GetString(item.Fields["Error Message"]);
        }

        protected override void UpdateItemFields(Item item)
        {
            base.UpdateItemFields(item);

            item.Fields["Public Key"].SetValue(PublicKey, true);
            item.Fields["Private Key"].SetValue(PrivateKey, true);
            item.Fields["Error Message"].SetValue(ErrorMessage, true);
        }
    }
}

Create the new view “GoogleRecaptcha.cshtml” and place under the “Views\FormBuilder\FieldTemplates”

@using Sitecore.ExperienceForms.Mvc.Html
@model Sitecore.Scientist.Forms.Fields.GoogleRecaptcha

https://www.google.com/recaptcha/api.js

<span class=”field-validation-error” id=”captchaError-@Html.IdFor(m => Model.Value)” style=”display:none”>@Model.ErrorMessage</span>
<input id=”@Html.IdFor(m => Model.Value)” name=”@Html.NameFor(m => Model.Value)” class=”@Model.CssClass” value=”@Model.Value” type=”hidden” data-sc-tracking=”@Model.IsTrackingEnabled” data-sc-field-name=”@Model.Name” @Html.GenerateUnobtrusiveValidationAttributes(m => m.Value) />
@Html.ValidationMessageFor(m => Model.Value)

if ($(‘#’+’@Html.IdFor(m => Model.Value)’).parent(‘form’)) {
$($(‘#’ + ‘@Html.IdFor(m => Model.Value)’).parent(‘form’)).submit(function () {
document.getElementById(‘captchaError-‘+’@Html.IdFor(m => Model.Value)’).style.display = “none”;
$(‘span[data-valmsg-for=”@Html.NameFor(m => Model.Value)’).hide();
var recaptchaResponse = grecaptcha.getResponse();
if (recaptchaResponse.length == 0) {
document.getElementById(‘captchaError-‘ + ‘@Html.IdFor(m => Model.Value)’).value = $(‘#’ + ‘@Html.IdFor(m => Model.Value)’).data(‘val-required’);
document.getElementById(‘captchaError-‘+’@Html.IdFor(m => Model.Value)’).style.display = “block”;
return false;
}
else {
document.getElementById(‘captchaError-‘+’@Html.IdFor(m => Model.Value)’).style.display = “none”;
$(‘#’+’@Html.IdFor(m => Model.Value)’).val(recaptchaResponse);
return true;
}
});
}
function captchaCallback() {
document.getElementById(‘captchaError-‘+’@Html.IdFor(m => Model.Value)’).style.display = “none”;
$(‘span[data-valmsg-for=”@Html.NameFor(m => Model.Value)’).hide();
if (recaptchaResponse.length == 0) {
document.getElementById(‘captchaError-‘ + ‘@Html.IdFor(m => Model.Value)’).value = $(‘#’ + ‘@Html.IdFor(m => Model.Value)’).data(‘val-required’);
document.getElementById(‘captchaError-‘+’@Html.IdFor(m => Model.Value)’).style.display = “block”;
}
else {
document.getElementById(‘captchaError-‘+’@Html.IdFor(m => Model.Value)’).style.display = “none”;
$(‘#’+’@Html.IdFor(m => Model.Value)’).val(recaptchaResponse);
}
}

Coding part is done .Its time to create the field.Create the new field  “Google Recaptcha” under the “/sitecore/system/Settings/Forms/Field Types/Security” path and update red marked field. Recaptcha field Recaptcha field2 Lets create the form Captcha form We are not done yet.Its very important to create the server side validator for the recaptcha. Create the new class called “CaptchaValidator.cs” like below,

using Newtonsoft.Json;
using Sitecore.Data;
using Sitecore.ExperienceForms.Mvc.Models.Fields;
using Sitecore.ExperienceForms.Mvc.Models.Validation;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Web.Mvc;

namespace Sitecore.Scientist.Forms.Validator
{
    public class CaptchaResponse
    {
        [JsonProperty("success")]
        public bool Success { get; set; }

        [JsonProperty("error-codes")]
        public List ErrorMessage { get; set; }
    }

    public class CaptchaValidator : ValidationElement
    {

        public override IEnumerable ClientValidationRules
        {
            get
            {
                if (string.IsNullOrEmpty(this.PrivateKey))
                {
                    yield break;
                }
            }
        }

        protected virtual string PrivateKey
        {
            get;
            set;
        }

        protected virtual string Title
        {
            get;
            set;
        }
        protected virtual string FieldName
        {
            get;
            set;
        }
        public CaptchaValidator(ValidationDataModel validationItem) : base(validationItem)
        {

        }

        public override void Initialize(object validationModel)
        {
            base.Initialize(validationModel);
            StringInputViewModel stringInputViewModel = validationModel as StringInputViewModel;
            if (stringInputViewModel != null)
            {
                var fieldItem = Context.Database.GetItem(ID.Parse(stringInputViewModel.ItemId));
               if(fieldItem!=null)
                {
                    this.PrivateKey = fieldItem["Private Key"];
                }
                this.Title = stringInputViewModel.Title;
                this.FieldName = stringInputViewModel.Name;
            }
        }

        public override ValidationResult Validate(object value)
        {
            if (value == null)
            {
                return ValidationResult.Success;
            }
            var isCaptchaValid = ValidateCaptcha((string)value, this.PrivateKey);
            if (!isCaptchaValid)
            {
                return new ValidationResult(this.FormatMessage(new object[] { this.Title }));
            }
            return ValidationResult.Success;
        }
        public static bool ValidateCaptcha(string response, string secret)
        {
            var client = new WebClient();
            var reply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, response));

            var captchaResponse = JsonConvert.DeserializeObject(reply);

            return System.Convert.ToBoolean(captchaResponse.Success);
        }
    }
}

Create the new Captchavalidator under the “/sitecore/system/Settings/Forms/Validations” like below screenshot

captchavalidator

Select the CaptchaValidator in GoogleRecaptcha Validations.

captchavalidator selection

we are done for creating the google recaptcha field .Let’s test the field

Captcha testing

1_CBiTTy_Wpw_r62BC7H7WMA

Thanks to Nikki Punjabi . I have referred his post and created this post.

Lazy developers don’t like to create this field from all of the above steps.That’s why i created the sitecore package for the lazy guys.Please download the package from this Google Recaptcha SC9 Form Field-V1.0.zip

After installing the package, if you are getting the below error then update your recaptcha “Private Key” and “Public Key”.

Captcha invalid domain

Download the source code from Github.

Thanks you for visiting my blog . Happy Coding.

Sitecore 9 Forms Part-1 – Form with Email Exist Validation

Hello Sitecorians,

Hope you’re playing around with Sitecore 9 and with the newly introduced cool features in it.There is lot to do with the Sitecore forms .I will explain that in upcoming posts.In this post i am going to explain the basic sitecore 9 form with Email exist validation.

Every subscription form email exist validation is very basic requirement.We can do that in very simple way.Please follow my steps,

How to do ?

  1. Create the new validator class “EmailExistValidator”
using Microsoft.Extensions.DependencyInjection;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.DependencyInjection;
using Sitecore.ExperienceForms.Data;
using Sitecore.ExperienceForms.Mvc.Models.Fields;
using Sitecore.ExperienceForms.Mvc.Models.Validation;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;

namespace Sitecore.Scientist.Forms.Validator
{
    public class EmailExistValidator : ValidationElement<string>
    {
        public readonly string FormTemplateId = "{6ABEE1F2-4AB4-47F0-AD8B-BDB36F37F64C}";

        private IFormDataProvider _dataProvider;


        protected virtual IFormDataProvider FormDataProvider
        {
            get
            {
                IFormDataProvider formDataProvider = this._dataProvider;
                if (formDataProvider == null)
                {
                    IFormDataProvider service = ServiceLocator.ServiceProvider.GetService<IFormDataProvider>();
                    IFormDataProvider formDataProvider1 = service;
                    this._dataProvider = service;
                    formDataProvider = formDataProvider1;
                }
                return formDataProvider;
            }
        }

        public override IEnumerable<ModelClientValidationRule> ClientValidationRules
        {
            get
            {
                if (string.IsNullOrEmpty(this.FormId))
                {
                    yield break;
                }
            }
        }

        protected virtual string FormId
        {
            get;
            set;
        }

        protected virtual string Title
        {
            get;
            set;
        }
        protected virtual string FieldName
        {
            get;
            set;
        }
        public EmailExistValidator(ValidationDataModel validationItem) : base(validationItem)
        {

        }

        public override void Initialize(object validationModel)
        {
            base.Initialize(validationModel);
            StringInputViewModel stringInputViewModel = validationModel as StringInputViewModel;
            if (stringInputViewModel != null)
            {
                var fieldItem = Context.Database.GetItem(ID.Parse(stringInputViewModel.ItemId));
                var formItem = GetFormItem(fieldItem);
                if (formItem != null)
                {
                    this.FormId = formItem.ID.ToString();
                }
                this.Title = stringInputViewModel.Title;
                this.FieldName = stringInputViewModel.Name;
            }
        }

        public override ValidationResult Validate(object value)
        {
            if (value == null)
            {
                return ValidationResult.Success;
            }
            if (!string.IsNullOrEmpty(this.FormId))
            {
                var formId = Guid.Parse(this.FormId);
                var data = this.FormDataProvider.GetEntries(formId, null, null);
                foreach (var item in data)
                {
                    var emailValue = item.Fields.Where(x => x.FieldName == this.FieldName).FirstOrDefault();
                    if (emailValue != null && emailValue.Value.ToLower() == value.ToString().ToLower())
                    {
                        return new ValidationResult(this.FormatMessage(new object[] { this.Title }));
                    }

                }
                return ValidationResult.Success;
            }
            return new ValidationResult(this.FormatMessage(new object[] { this.Title }));
        }
        public Item GetFormItem(Item sitecoreItem)
        {
            if (sitecoreItem == null)
                return null;
            Item parent = sitecoreItem.Parent;
            if (parent.TemplateID == ID.Parse(this.FormTemplateId))
            {
                return parent;
            }
            else if (ItemIDs.RootID == parent.ID)
            {
                return null;
            }
            else
            {
                return GetFormItem(parent);
            }
        }
    }
}

2. Now we have to create the EmailExistValidator under the “/sitecore/system/Settings/Forms/Validations” in master database.

3. Just duplicate the Emailvalidator and update the values like below screenshot

EmailExistValidator

4. Create the new form with Email field ,

Subscribe form

5. Add the newly created “EmailExistValidator” validation to the email field.

add validation

6 . We are almost done.Lets go for the testing.

Exist Validation

415w

Thanks for visiting my blog.Download the source code from Github.