Creating a Custom field type in Umbraco to supports the invisible enterprise reCaptcha

112 Views Asked by At

Hello Stackoverflow community.

Currently, I found that Umbraco doesn't have an option to support the enterprise invisible reCaptcha. After following a bit along I did came across this documentation where we can create custom field type and have a custom validation logic.

What I have done so far :

Add a new class

ReCaptchaEnterprise.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Core.Models;
using Umbraco.Forms.Core.Services;

namespace FormsExtensions
{
    public class ReCaptchaEnterprise : Umbraco.Forms.Core.FieldType
    {
        public ReCaptchaEnterprise()
        {
            Id = new Guid("e3ab6a21-bc99-4cb8-999e-ad01203d61bd"); // Replace this!
            Name = "ReCaptcha Enterprise";
            Description = "Render a custom reCaptcha enterprise field.";
            Icon = "icon-autofill";
            SortOrder = 10;
            FieldTypeViewName = "FieldType.ReCaptchaEnterprise.cshtml";
        }
        
        // Custom validation in here which will occur when the form is submitted.
        // Any strings returned will cause the submission to be considered invalid.
        // Returning an empty collection of strings will indicate that it's valid to proceed.
        public override IEnumerable<string> ValidateField(Form form, Field field, IEnumerable<object> postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, IFieldTypeStorage fieldTypeStorage)
        {
            var returnStrings = new List<string>();

            if (!postedValues.Any(value => value.ToString().ToLower().Contains("custom")))
            {
                returnStrings.Add("You need to include 'custom' in the field!");
            }

            var reCaptchaResponse = context.Request.Form["g-recaptcha-response"].ToString();
            Console.WriteLine("+++++++++++++++++++++ReCaptcha response+++++++++++++++++++++++++++" + reCaptchaResponse);
            
            // Also validate it against the default method (to handle mandatory fields and regular expressions)
            return base.ValidateField(form, field, postedValues, context, placeholderParsingService, fieldTypeStorage, returnStrings);
        }
    }    
}

Partial View

FieldType.ReCaptchaEnterprise.html

@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage

@using Umbraco.Forms.Web
@using Microsoft.Extensions.Configuration
@model Umbraco.Forms.Web.Models.FieldViewModel
@inject IConfiguration Configuration
@{
    var siteKey = Configuration.GetSection("ReCaptchaEnterprise")["SiteKey"];
    if (!string.IsNullOrEmpty(siteKey))
    {
        Html.AddFormThemeScriptFile("https://www.google.com/recaptcha/enterprise.js?render=" + siteKey);
        <div class="g-recaptcha"
             data-sitekey="@siteKey"
             data-callback="onSubmit" data-size="invisible">
        </div>
         <input type="hidden" name="g-recaptcha-response" />
         
        <script type="application/javascript">
            function  onSubmit(event){
                event.preventDefault();
                grecaptcha.execute();
            }
        </script>
    }
    else
    {
        <p class="error">ERROR: reCAPTCHA is missing the Site Key. Please update the configuration to include a value.</p>
    }
} 

Registering new field as dependency

using FormsExtensions;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Forms.Core.Providers;

namespace Web
{
    public class ReCaptchaEnterpriseComposer: IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.WithCollectionBuilder<FieldCollectionBuilder>()
                .Add<ReCaptchaEnterprise>();
        }
    }    
} 

Umbraco backoffice view

App_plugins/UmbracoFotms/backoffice/common/fieldtypes/ReCaptchaEnterprise.html

<div class="g-recaptcha" data-sitekey="<site-key>" data-size="invisible"></div>

I am able to add the reCaptcha enterprise in the form. However, when I go to Content and add it over there via Rich Text Editor > Macro the following exception is thrown.

Received an error from the server An error occurred Cannot bind source type Umbraco.Forms.Web.Models.FieldViewModel to model type Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent.

Exception Details Umbraco.Cms.Web.Common.ModelBinders.ModelBindingException, Umbraco.Web.Common, Version=12.0.1.0, Culture=neutral, PublicKeyToken=null: Cannot bind source type Umbraco.Forms.Web.Models.FieldViewModel to model type Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent.

I have no idea what is going wrong I am debugging the issue since last 2 days cannot find any solution.

I will really appreciate any leads to resolve this issue or if you can share your experience regarding how you created a custom field in Umbraco. Thank you so much for your time for reading through my question until here.

I have tried following other packages that have done something similar to implement reCaptcha but it is of no help until now.

2

There are 2 best solutions below

0
user23745000 On BEST ANSWER

If someone faces similar issue. I have removed @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage from the Partial View and now everything started working.

4
Nurhak Kaya On

Your implementation seems correct; apart from that, I can't see how you have registered your new field as a dependency, which could be the issue here. See a sample code below for registering your new field.

If this doesn't fix your problem, then my recommendation is to create a simpler answer type following this official documentation, and then update it for your requirements step by step, until it works as you expect.

Here is another example about creating or updating field types: https://dev.to/andy_boot/umbraco-forms-add-additional-attributes-to-existing-field-types-4abg

Recaptcha examples: https://docs.umbraco.com/umbraco-forms/editor/creating-a-form/fieldtypes

using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Forms.Core.Providers;

    namespace MyFormsExtensions
    {
        public class Startup : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.WithCollectionBuilder<FieldCollectionBuilder>()
                    .Add<MyCustomField>();
            }
        }
    }