Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Apr 02, 2015 @ 14:58
    Andy Butland
    3

    Upgrading ReCaptcha to the "I'm not a robot" version

    I'm picking up an issue where we are looking to upgrade to the latest version of ReCaptch (the one that gives you just a simulated check box to click on to say "I'm not a robot").

    We've modified FieldType.Recaptcha.cshtml as follows (basically removing the default and adding in the code to render the ReCaptcha):

    @using Umbraco.Forms.Core;
    @model Umbraco.Forms.Mvc.Models.FieldViewModel
    @{
        var siteKey = Configuration.GetSetting("RecaptchaPublicKey");
        if (!string.IsNullOrEmpty(siteKey))
        {
            var theme = "clean";
            var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
            if (fieldSettingViewModel != null)
            {
                theme = fieldSettingViewModel.Value;
            }
            <script src="https://www.google.com/recaptcha/api.js" async defer></script>
            <div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div>
        }
    }
    @*@ReCaptchaHelper.ReCaptcha(theme)*@

    Seems that it's not as simple as that though, as if I make the field Mandatory I always get the validation error message (whether or not I've said I'm not a robot); and if I don't make it Mandatory I don't get blocked even if I don't complete the ReCaptcha field.

    Initially just wondering if this is even feasible without some change to Contour core and how it handles the valiation of the ReCaptcha field?

    Thanks in advance for any advice.

    Andy

  • Daniel Larsen 116 posts 381 karma points
    Apr 03, 2015 @ 10:06
    Daniel Larsen
    0

    I am looking for the exact same thing. Just following where this is going :-)

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Apr 04, 2015 @ 23:26
    Andy Butland
    107

    Seems that it doesn't work out of the box, but with some custom validation I've managed to get it working.  For background this blog post from Tim and this Code Project article was useful in setting it up.

    As noted above I've amended the default form field rendering - in FieldTyle.Recaptcha.cshtml - as follows:

    @using Umbraco.Forms.Core;
    
    @model Umbraco.Forms.Mvc.Models.FieldViewModel
    @{
        var siteKey = Configuration.GetSetting("RecaptchaPublicKey");
        if (!string.IsNullOrEmpty(siteKey))
        {
            var theme = "clean";
            var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
            if (fieldSettingViewModel != null)
            {
                theme = fieldSettingViewModel.Value;
            }
    }
    }

    And then I've wired up custom validation on an application event like this:

    public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        FormRenderController.FormValidate += ContourEvents.FormValidate;
    }
    
    public static class ContourEvents
    {
        public static void FormValidate(object sender, FormViewEventArgs e)
        {
            var reCaptchaField = e.Form.AllFields
                .SingleOrDefault(f => f.FieldType.Name == "Recaptcha");
            if (reCaptchaField != null)
            {
                var httpContext = HttpContext.Current;
                var secretKey = Configuration.GetSetting("RecaptchaPrivateKey");
                var reCaptchaResponse = httpContext.Request["g-recaptcha-response"];
    
                var url = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", 
    secretKey, reCaptchaResponse); using (var client = new WebClient()
    { var response = client.DownloadString(url); var captchaResponse = JsonConvert.DeserializeObject(response); if (!captchaResponse.Success) { var controller = sender as Controller; controller.ModelState.AddModelError(reCaptchaField.Id.ToString(), "Validation not accepted"); }
    } } } }

    This looks to work as expected.  If you ignore the CAPTCHA field, the response in the form is empty and so the call to Google comes back unsuccessful. If you successfully prove you aren't a robot, it'll come back with a success response.

  • Graham Carr 277 posts 389 karma points
    Jul 31, 2015 @ 13:24
    Graham Carr
    0

    Hello,

    Sorry if this is a silly question but where do you go about putting the custom validation, do you have to create a separate class project and reference it or is it just placed in the App_Code folder, can you provide a full class file structure with references as well please if possible?

    Thanks.

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Jul 31, 2015 @ 13:46
    Andy Butland
    0

    Should work either way Graham. I tend to have a separate class library but it should work in App_Code just the same. Having a brain fade about exactly what project I did this in I'm afraid so can't put my hands on it now. But think you'll need a reference to Umbraco.Forms.Mvc.

    Andy

  • Graham Carr 277 posts 389 karma points
    Jul 31, 2015 @ 15:09
    Graham Carr
    0

    Thanks Andy, if you do happen to locate the project that you did this in, I would very much welcome if you could send me the code including the customised version FieldTyle.Recaptcha.cshtml.

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Jul 31, 2015 @ 20:56
    Andy Butland
    0

    Have found it, it's what I have above. The only bit you're missing maybe is the using statements - you'll likely need some of:

    using System;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Script.Serialization;
    using Umbraco.Core;
    using Umbraco.Core.Models;
    using Umbraco.Core.Services;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Mvc;
    using Umbraco.Forms.Mvc.Controllers;
    using Umbraco.Web;
    using Newtonsoft.Json;
    

    Likely not all as I have a bit more going on in my class.

  • Heather Floyd 610 posts 1032 karma points MVP 6x c-trib
    Feb 04, 2016 @ 21:42
    Heather Floyd
    7

    Based on Andy's excellent post, I have made a few updates/enhancements. Code files copied here for anyone who is looking for "plug-n-play".

    UmbracoFormsEvents.cs

    (Compiled with other custom code, or added to App_Code directory)

    namespace MYSITE.Events
    {
        using System.Collections.Generic;
        using System.Linq;
        using System.Net;
        using System.Web;
        using System.Web.Mvc;
        using Newtonsoft.Json.Linq;
        using Umbraco.Core;
        using Umbraco.Core.Logging;
        using Umbraco.Forms.Mvc;
        using Umbraco.Forms.Web.Controllers;
    
        public class UmbracoFormsEvents : ApplicationEventHandler
        {
            //Requires Web.config app keys:
            //     <add key="RecaptchaSiteKey" value="XXXX" />
            //     <add key="RecaptchaSecretKey" value="XXXX" />
    
            protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
            {
                UmbracoFormsController.FormValidate += this.FormValidate;
                //LogHelper.Info<UmbracoFormsEvents>("ReCaptcha FormValidate Event added to Umbraco Forms.");
            }
    
            private void FormValidate(object sender, FormValidationEventArgs e)
            {
                //LogHelper.Info<UmbracoFormsEvents>("FormValidate with ReCaptcha Running...");
                var matchingFields = e.Form.AllFields.Where(f => f.FieldType.Name.Contains("Recaptcha"));
                if (matchingFields.Any())
                {
                    var reCaptchaField = matchingFields.FirstOrDefault();
    
                    var httpContext = HttpContext.Current;
                    var secretKey = System.Configuration.ConfigurationManager.AppSettings["RecaptchaSecretKey"];
    
                    if (secretKey == "")
                    {
                        LogHelper.Warn<UmbracoFormsEvents>("ERROR: ReCaptcha v.2 is missing the Secret Key - Please update the web.config to include 'key=\"RecaptchaSecretKey\"'");
                    }
                    else
                    {
                        var reCaptchaResponse = httpContext.Request["g-recaptcha-response"];
    
                        var url = string.Format(
                            "https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
                            secretKey,
                            reCaptchaResponse);
    
                        var isSuccess = false;
                        var errorCodes = new List<string>();
    
                        using (var client = new WebClient())
                        {
                            var response = client.DownloadString(url);
    
                            JObject responseParsed = JObject.Parse(response);
    
                            //Get Success Status
                            JToken sucessToken;
                            var sucessFound = responseParsed.TryGetValue("success", out sucessToken);
                            if (sucessFound)
                            {
                                isSuccess = sucessToken.Value<bool>();
                            }
    
                            //Get Error codes
                            JToken errorsToken;
                            var errorsFound = responseParsed.TryGetValue("error-codes", out errorsToken);
                            if (errorsFound)
                            {
                                var errorsChildren = errorsToken.Children();
                                foreach (var child in errorsChildren)
                                {
                                    errorCodes.Add(child.Value<string>());
                                }
                            }
                            else
                            {
                                errorCodes.Add("unknown-error");
                            }
                        }
    
                        if (!isSuccess)
                        {
                            var controller = sender as Controller;
    
                            var compiledErrors = ",";
    
                            foreach (var code in errorCodes)
                            {
                                //TODO: Use Dictionary Keys to return error message text
                                compiledErrors += ", " + code;
                            }
                            compiledErrors = compiledErrors.Replace(",,", "");
    
                            //Add errors to Form Model
                            var errorMsg = string.Format("Recaptcha Verification Failed: {0}", compiledErrors);
                            controller.ModelState.AddModelError(reCaptchaField.Id.ToString(),errorMsg);
                        }
                    }
                }
            }
    
        }
    }
    

    \Views\Partials\Forms\Fieldtypes\Fieldtype.Recaptcha.cshtml

    @using Umbraco.Core.Logging
    @using Umbraco.Forms.Core;
    @using MYSITE.Events
    
    @model Umbraco.Forms.Mvc.Models.FieldViewModel
    
    @{
        //Updated Jan 2016 to use new ReCaptcha 2.0
    
        var siteKey = System.Configuration.ConfigurationManager.AppSettings["RecaptchaSiteKey"];
    
        if (siteKey == "")
        {
            LogHelper.Warn<UmbracoFormsEvents>("ERROR: ReCaptcha v.2 is missing the Site Key - Please update the web.config to include 'key=\"RecaptchaSiteKey\"'");
        }
        else
        {
            //LogHelper.Info<UmbracoFormsEvents>("ReCaptcha Site Key:" + siteKey);
            if (!string.IsNullOrEmpty(siteKey))
            {
                var theme = "clean";
                var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
                if (fieldSettingViewModel.Value != "")
                {
                    theme = fieldSettingViewModel.Value;
                }
    
                <script src="https://www.google.com/recaptcha/api.js" async defer></script>
                <div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div>
            }
        }
    }
    
  • Chris Van Oort 110 posts 371 karma points
    Feb 11, 2016 @ 22:43
    Chris Van Oort
    0

    Heather thanks for posting some code!

    I'm running Umbraco Forms 4.1.5 but I'm missing a couple of the references you have shown in UmbracoFormsEvents.cs

    using Umbraco.Forms.Mvc;
    using Umbraco.Forms.Web.Controllers;
    

    Any ideas?

    Update: Looks like including a reference in the Visual Studio project to Umbraco.Forms.Web.dll provides visibility into those namespaces for compilation. I suspect others who haven't installed via Nuget and have installed via Umbraco package manager are missing those as well.

  • David Conlisk 432 posts 1008 karma points
    Jun 14, 2016 @ 11:23
    David Conlisk
    0

    Hi Heather, Andy,

    This is excellent, thanks for sharing! Do you think there is a way to read the Recaptcha keys from the /App_Plugins/UmbracoForms/UmbracoForms.config file instead of needing to duplicate them in the web.config?

    Thanks,

    David

  • Si Burgess 15 posts 56 karma points
    Sep 16, 2016 @ 15:27
    Si Burgess
    1
    var secretKey = Umbraco.Forms.Core.Configuration.GetSetting("RecaptchaPrivateKey");
    var siteKey = Umbraco.Forms.Core.Configuration.GetSetting("RecaptchaPublicKey");
    
  • Bo Jacobsen 607 posts 2405 karma points
    Aug 16, 2017 @ 07:49
    Bo Jacobsen
    0

    Hi Heather.

    I know its an old post.

    But how can i change the Theme prevalues for recaptcha?

    var theme = "clean";
    var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => 
    x.Key == "Theme");
    if (fieldSettingViewModel.Value != "")
    {
            theme = fieldSettingViewModel.Value;
    }
    
  • Heather Floyd 610 posts 1032 karma points MVP 6x c-trib
    Feb 11, 2016 @ 23:12
    Heather Floyd
    0

    Hi Chris,

    Yep - you found it "Umbraco.Forms.Web.dll". I have a secondary compiled "code" project and did use NuGet to access the libraries.

    http://www.nuget.org/packages/UmbracoForms.Core/ for anyone who is looking for the right package.

  • Chris Van Oort 110 posts 371 karma points
    Feb 12, 2016 @ 19:17
    Chris Van Oort
    0

    Have you seen this javascript error before? (Error copied from Chrome Console)

    Uncaught Error: ReCAPTCHA placeholder element must be empty
         Ko @  recaptcha__en.js:338
         (anonymous function) @ recaptcha__en.js:341
         no @ recaptcha__en.js:324
         (anonymous function) @ recaptcha__en.js:340
         (anonymous function) @ recaptcha__en.js:350
    

    I'm using same empty tag you are in Fieldtype.Recaptcha.cshtml:

    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
    <div class="g-recaptcha" data-sitekey="@recaptchaPublicKey" data-theme="@theme"></div>
    

    UPDATE: In case anyone else has this issue, I was receiving the above error because the "api.js" for ReCaptcha was being included in my templates already.

  • Tom T 2 posts 22 karma points
    Sep 27, 2016 @ 01:11
    Tom T
    0

    Big thanks to Heather and Andy, this solution saved me some time today.

    Just a note that if you happen to have the strange requirement to have 2 forms on the page, your second form may not show the second Recaptcha.

    In this case I tweaked the FieldType.Recaptcha.cshtml to allow for multiple recaptcha fields with the following:

            <script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>
            <div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div>
    
            <script type="text/javascript">
                var CaptchaCallback = function () {
                    $('.g-recaptcha').each(function (index, el) {
                        grecaptcha.render(el, { 'sitekey': '@siteKey' });
                    });
                };
            </script>
    
  • Ran Chen 40 posts 117 karma points
    Mar 13, 2017 @ 21:51
  • Santhosh 58 posts 99 karma points
    Apr 05, 2018 @ 12:20
    Santhosh
    0

    Hi Paulis, Add references of the following:

    Umbraco.Forms.CodeFirst.dll Umbraco.Forms.Core.dll Umbraco.Forms.Core.Providers.dll Umbraco.Forms.Core.Providers.V6.dll Umbraco.Forms.Core.Providers.V7.dll Umbraco.Forms.Mvc.dll Umbraco.Forms.References.dll Umbraco.Forms.UI.dll

  • Santhosh 58 posts 99 karma points
    Apr 06, 2018 @ 09:47
    Santhosh
    0

    Hi All,

    Thanks for the code. But I see a problem that recaptcha field isn't checked for mandatoryness. I was able to submit the form without selecting the recaptcha. Anybody has found the same problem?

    Regards

    Santhosh

  • Santhosh 58 posts 99 karma points
    Apr 06, 2018 @ 14:44
    Santhosh
    0

    Sorry it was my fault. I didnt hook the eventhandler correctly. Now its fixed.

    Thanks again

    Regards,

    Santhosh

  • J 447 posts 864 karma points
    May 03, 2018 @ 15:58
    J
    0

    I decided to create a class with the above code, using @AndyButland the library compiled but when i copied to the bin directory nothing happened. I then copied @HeatherFloyd code along with copying the UmbracoForms dlls from https://our.umbraco.org/projects/developer-tools/umbraco-forms/ but had an error

    The type or namespace name 'UmbracoFormsEvents' could not be found (are you missing a using directive or an assembly reference?)

    Before i dive into this any further would this resolve the issue mentioned for Contour? Along with the version i am using? Or is this strictly for Umbraco Forms?

  • Heather Floyd 610 posts 1032 karma points MVP 6x c-trib
    May 09, 2018 @ 15:54
    Heather Floyd
    0

    Hi @J,

    "UmbracoFormsEvents" was the name of the class in my custom file:

    public class UmbracoFormsEvents : ApplicationEventHandler 
    {...}
    

    If you named your class differently, that would explain it. :-)

  • Santhosh 58 posts 99 karma points
    May 04, 2018 @ 08:42
    Santhosh
    0

    Hi J,

    I have done it for 7.5.14 (Web Forms) + Contour v3.0.31. So it should work.

    Here is my code:

    /// <summary>
    /// Extended umbraco global asax (application events) to include custom recaptcha validation for EU 
    /// </summary>
    public class ExtendedUmbracoGlobal : UmbracoGlobal
    {
        /// <summary>
        /// Eventhandler that gets executed when application gets started
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnApplicationStarted(object sender, EventArgs e)
        {
            base.OnApplicationStarted(sender, e);
            FormRenderController.FormValidate += FormValidate;
        }
    
        /// <summary>
        /// Event handler to facilitate the coutour form  validation for recaptcha v2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormValidate(object sender, FormViewEventArgs e)
        {
            var reCaptchaField = e.Form.AllFields.SingleOrDefault(f => f.FieldType.Name == "Recaptcha");
            if (reCaptchaField != null)
            {
                var httpContext = HttpContext.Current;
                if (httpContext == null) return;
                var secretKey = ConfigurationManager.AppSettings["RecaptchaSecretKey"];
                if (string.IsNullOrEmpty(secretKey)) throw new EcomApplicationException("Recaptcha v2 secret key is not configurd in web.config");
                var reCaptchaResponse = httpContext.Request["g-recaptcha-response"];
    
                var url = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
                    secretKey, reCaptchaResponse);
                var isSuccess = false;
                var errorCodes = new List<string>();
                using (var client = new WebClient())
                {
                    var response = client.DownloadString(url);
                    JObject responseParsed = JObject.Parse(response);
    
                    //Get Success Status
                    JToken sucessToken;
                    var sucessFound = responseParsed.TryGetValue("success", out sucessToken);
                    if (sucessFound)
                    {
                        isSuccess = sucessToken.Value<bool>();
                    }
    
                    //Get Error codes
                    JToken errorsToken;
                    var errorsFound = responseParsed.TryGetValue("error-codes", out errorsToken);
                    if (errorsFound)
                    {
                        var errorsChildren = errorsToken.Children();
                        foreach (var child in errorsChildren)
                        {
                            errorCodes.Add(child.Value<string>());
                        }
                    }
                    else
                    {
                        errorCodes.Add("unknown-error");
                    }
                }
    
                if (!isSuccess)
                {
                    var controller = sender as Controller;
    
                    var compiledErrors = ",";
    
                    foreach (var code in errorCodes)
                    {
                        //TODO: Use Dictionary Keys to return error message text
                        compiledErrors += ", " + code;
                    }
                    compiledErrors = compiledErrors.Replace(",,", "");
    
                    //Add errors to Form Model
                    var errorMsg = string.Format("Recaptcha Verification Failed: {0}", compiledErrors);
                    controller.ModelState.AddModelError(reCaptchaField.Id.ToString(), errorMsg);
                }
            }
    
        }
    }
    

    Hope it helps

  • Santhosh 58 posts 99 karma points
    May 04, 2018 @ 08:43
    Santhosh
    0

    Here are the using statements:

    using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net; using System.Web; using System.Web.Mvc; using Newtonsoft.Json.Linq; using Umbraco.Forms.Mvc; using Umbraco.Forms.Mvc.Controllers;

  • J 447 posts 864 karma points
    May 08, 2018 @ 15:47
    J
    0

    Could you also post your Fieldtype.Recaptcha.cshtml as well please? I have the class compiling but changing UmbracoGlobal to the one listed in Global.asax however i am not seeing any changes or breakpoints hitting.

  • Santhosh 58 posts 99 karma points
    May 09, 2018 @ 07:21
    Santhosh
    0

    Here is my FieldType.Recaptcha.cshtml:

    @using Umbraco.Forms.Core;
    @using System.Configuration;
    
    @model Umbraco.Forms.Mvc.Models.FieldViewModel
    @{
        var siteKey = System.Configuration.ConfigurationManager.AppSettings["RecaptchaSiteKey"];
        if (string.IsNullOrEmpty(siteKey))
        {
            <div>ReCaptcha v.2 is missing the Site Key</div>
        }
        else
        {
            //LogHelper.Info<UmbracoFormsEvents>("ReCaptcha Site Key:" + siteKey);
            if (!string.IsNullOrEmpty(siteKey))
            {
                var theme = "clean";
                var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
                if (fieldSettingViewModel.Value != "")
                {
                    theme = fieldSettingViewModel.Value;
                }
                <script src="https://www.google.com/recaptcha/api.js" async defer></script>
                <div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div>
            }
        }
    
    }
    

    UmbracoGlobal is my own class derived from Umbraco.Web.UmbracoApplication class (customised umbraco application class that holds the application events)

    So its simple, you must make sure you have a new event handler - FormValidate (in my case, but it can be any method name) and add this event handler to Form Validate event of the umbraco contour forms during the application started event.

    Normally, your Global.asax file on the root folder is derived from Umbraco.Web.UmbracoApplication. But I have it as ExtendedUmbracoGlobal to have this working.

    Hope it helps!

    Thanks,

    Santhosh

  • J 447 posts 864 karma points
    May 09, 2018 @ 09:27
    J
    0

    I have the code as you have suggested.

    My FieldType.Recaptcha.cshtml is found under Umbraco>Plugins>UmbracoContour>Views

    I add a form to my homepage which has no Recaptcha (old version). In debug mode OnApplicationStarted never hits neither does FormValidate method.

    When designing the form i dont see a Recpatcha 2 option either, only Recpatcha so i readd Recaptcha (i assume changing the FieldType.Recaptcha.cshtml would display version 2)?

    but the new captcha never displays? I think im missing something obvious? Thanks for your help Santhosh

  • Santhosh 58 posts 99 karma points
    May 09, 2018 @ 09:48
    Santhosh
    0

    Umbraco>Plugins>UmbracoContour>Views is the correct place for the cshtml file.

    You don't see any difference on your form editor with recaptcha 2. It looks like this

    enter image description here

    enter image description here

    And you must see something like this rendered on your form when viewed:

    enter image description here

    Can you show me the class how you have added the event handler "FormValidate" to the event "FormRenderController.FormValidate"?

    Also show me the Global.asax content of your?

    Did you setup the RecaptchaSiteKey and RecaptchaSecretKey on your appSettings?

  • J 447 posts 864 karma points
    May 09, 2018 @ 10:33
    J
    0

    I've added the exact same class as yours, only difference i have is adding Umbraco.Web.UmbracoApplication instead of UmbracoGlobal, so the code is exactly the same.

    Global.asax - <%@ Application Inherits="Umbraco.Web.UmbracoApplication" Language="C#" %>
    

    I have not setup a sitekey or secret key yet as i was looking for an error (to indicate its working) but when i view the form i only see the old captcha

  • Santhosh 58 posts 99 karma points
    May 09, 2018 @ 11:31
    Santhosh
    0

    We are close. Can you tell me the class name where you hooked the event handler with the event? For example, in my case - ExtendedUmbracoGlobal

    This is the class that you must inherit on your Global.asax.

  • J 447 posts 864 karma points
    May 09, 2018 @ 14:11
    J
    0

    I have the Global file inheriting from my new class and i can now see it hitting the OnApplicationStarted code.... however the form still displays the old captcha.

  • J 447 posts 864 karma points
    May 11, 2018 @ 15:32
    J
    0

    Anyone?

Please Sign in or register to post replies

Write your reply to:

Draft