+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

CAPTCHA for Sitefinity Forms

by Radoslav Georgiev

Recently there have been a number of requests for adding a CAPTCHA to the Sitefinity Forms widgets. Since the standard CAPTCHA controls that work with postback are not suitable for the Forms control, I am going to walk you through the process of implementing a client-side CAPTCHA control which can be used on Sitefinity Forms. I've stumbled on this blog post: 8 jQuery CAPTCHA Plugin with Tutorial, listing different jQuery CAPTCHA libraries. For this article I chose to use the first plugin(QapTcha), however the same principle applies for the rest of the plugins and you can use any of them. Now let's proceed to the implementation part.

Implementing the CAPTCHA widget

We are going to implement our custom widget as a script control, so we need to inherit from SimpleScriptView. This will allow us to use our control not only for Forms, but also for other places where we need CAPTCHA. The process of implementation is the same as the one for Book Widget part of the Siteifnity SDK, minus the implementation of a Silverligh control.

Implementing the server-side class

The only logic that our server side class will hold are just a few properties that are used by the QapTcha plugin to set up the UI. We need to expose those properties, set up the layout template path for the widget and refer the scripts on which this plugin depends. You can find the class implementation bellow:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using Telerik.Sitefinity.Modules.Pages;
using Telerik.Sitefinity.Web.UI;
 
namespace Telerik.Sitefinity.Samples.Forms
{
    class FormQapTchaControl: SimpleScriptView
    {
        #region Properties
 
        protected override string LayoutTemplateName
        {
            get { return null; }
        }
 
        public override string LayoutTemplatePath
        {
            get
            {
                return this.layoutTemplatePath;
            }
            set
            {
                this.layoutTemplatePath = value;
            }
        }
 
        [Category("QapTcha properites")]
        public bool AutoRevert
        {
            get
            {
                return this.autoRevert;
            }
            set
            {
                this.autoRevert = value;
            }
        }
 
        [Category("QapTcha properites")]
        public bool DisabledSubmit
        {
            get
            {
                return this.disabledSubmit;
            }
            set
            {
                this.disabledSubmit = value;
            }
        }
 
        [Category("QapTcha properites")]
        public string TxtLock
        {
            get
            {
                return this.txtLock;
            }
            set
            {
                this.txtLock = value;
            }
        }
 
 
        [Category("QapTcha properites")]
        public string TxtUnlock
        {
            get
            {
                return this.txtUnlock;
            }
            set
            {
                this.txtUnlock = value;
            }
        }
 
        #endregion
 
        [Browsable(false)]
        public HtmlGenericControl QapTchaDiv
        {
            get
            {
                return this.Container.GetControl<HtmlGenericControl>("QaptCha", true);
            }
        }
 
        #region Methods
 
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            PageManager.ConfigureScriptManager(this.Page, ScriptRef.JQueryUI);
        }
 
        public override IEnumerable<System.Web.UI.ScriptDescriptor> GetScriptDescriptors()
        {
            var descriptors = new List<ScriptDescriptor>();
 
            var descriptor = new ScriptControlDescriptor(typeof(FormQapTchaControl).FullName, this.ClientID);
            //pass property values to the script component
            descriptor.AddProperty("qapTchaDiv", this.QapTchaDiv.ClientID);
            descriptor.AddProperty("autoRevert", this.AutoRevert);
            descriptor.AddProperty("disabledSubmit", this.DisabledSubmit);
            descriptor.AddProperty("txtLock", this.TxtLock);
            descriptor.AddProperty("txtUnlock", this.TxtUnlock);
            descriptors.Add(descriptor);
 
            return descriptors;
        }
 
        public override IEnumerable<System.Web.UI.ScriptReference> GetScriptReferences()
        {
            var scripts = new List<ScriptReference>()
            {
                new ScriptReference(FormQapTchaControl.componentScriptPath, typeof(FormQapTchaControl).Assembly.FullName),
                new ScriptReference(FormQapTchaControl.qapTchaScript, typeof(FormQapTchaControl).Assembly.FullName),
                new ScriptReference(FormQapTchaControl.jqueryUITouchScript, typeof(FormQapTchaControl).Assembly.FullName)
            };
            return scripts;
        }
         
        protected override void InitializeControls(GenericContainer container)
        {
        }
 
        #endregion
 
        #region Private fields
 
        public string layoutTemplatePath = "~/SfControls/Telerik.Sitefinity.Samples.Forms.Resources.Views.FormQapTchaControl.ascx";
        private bool autoRevert = true;
        private bool disabledSubmit = true;
        private string txtLock = "Locked : form can't be submited";
        private string txtUnlock = "Unlocked : form can be submited";
        private static string componentScriptPath = "Telerik.Sitefinity.Samples.Forms.Resources.Scripts.FormQapTchaControl.js";
        private static string jqueryUITouchScript = "Telerik.Sitefinity.Samples.Forms.Resources.Scripts.jquery.ui.touch.js";
        private static string qapTchaScript = "Telerik.Sitefinity.Samples.Forms.Resources.Scripts.QapTcha.jquery.js";
        #endregion
 
    }
}

Implement the client-side component

The purpose of the client side component is to simply initialize our QapTcha plugin. We need to provide getter and setter methods for our properties, and pass them to the plugin:

Type.registerNamespace("Telerik.Sitefinity.Samples.Forms");
Telerik.Sitefinity.Samples.Forms.FormQapTchaControl = function (element) {
    Telerik.Sitefinity.Samples.Forms.FormQapTchaControl.initializeBase(this, [element]);
    this._qapTchaDiv = null;
    this._autoRevert = null;
    this._disabledSubmit = null;
    this._txtLock = null;
    this._txtUnlock = null;
}
 
Telerik.Sitefinity.Samples.Forms.FormQapTchaControl.prototype = {
    initialize: function () {
        Telerik.Sitefinity.Samples.Forms.FormQapTchaControl.callBaseMethod(this, 'initialize');
        //set QapTcha settings
        $('#' + this.get_qapTchaDiv()).QapTcha(
                                        { disabledSubmit: this.get_disabledSubmit(),
                                          autoRevert: this.get_autoRevert(),
                                          txtLock: this.get_txtLock(),
                                          txtUnlock: this.set_txtUnlock()
                                         });
    },
    dispose: function () {
        Telerik.Sitefinity.Samples.Forms.FormQapTchaControl.callBaseMethod(this, 'dispose');
    },
    // properties
    get_qapTchaDiv: function () { return this._qapTchaDiv; },
    set_qapTchaDiv: function (value) { this._qapTchaDiv = value; },
 
    get_autoRevert: function () { return this._autoRevert; },
    set_autoRevert: function (value) { this._autoRevert = value; },
 
    get_disabledSubmit: function () { return this._disabledSubmit; },
    set_disabledSubmit: function (value) { this._disabledSubmit = value; },
 
    get_txtLock: function () { return this._txtLock; },
    set_txtLock: function (value) { this._txtLock = value; },
 
    get_txtUnlock: function () { return this._txtUnlock; },
    set_txtUnlock: function (value) { this._txtUnlock = value; }
}
Telerik.Sitefinity.Samples.Forms.FormQapTchaControl.registerClass('Telerik.Sitefinity.Samples.Forms.FormQapTchaControl', Sys.UI.Control); 

 Implementing the control template

 The control template is used to refer the style sheet that defines the look an feel of the QapTcha plugin and define a placeholder div for the slider:

<%@ Control Language="C#" AutoEventWireup="true" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" Assembly="Telerik.Sitefinity" %>
 
<sf:ResourceLinks id="resourcesLinks1" runat="server" UseEmbeddedThemes="true">
  <sf:ResourceFile Name="Telerik.Sitefinity.Samples.Forms.Resources.CSS.QapTcha.jquery.css" AssemblyInfo="Telerik.Sitefinity.Samples.Forms.FormQapTchaControl, Telerik.Sitefinity.Samples.Forms" Static="true"/>
</sf:ResourceLinks>
 
<div id="QaptCha" class="QapTcha" runat="server"></div>

Project overview

After you have implemented your client-side and server-side components, the control template, and have added the dependent scripts, styles and images for the QapTcha plugin the project should look similar to the on the bellow image:

The last step in terms of the project is to register the embedded resources (CSS, image and script files) with our assembly. To do so edit the AssemblyInfo class:

[assembly: WebResource("Telerik.Sitefinity.Samples.Forms.Resources.Scripts.FormQapTchaControl.js", "application/x-javascript")]
[assembly: WebResource("Telerik.Sitefinity.Samples.Forms.Resources.Scripts.jquery.ui.touch.js", "application/x-javascript")]
[assembly: WebResource("Telerik.Sitefinity.Samples.Forms.Resources.Scripts.QapTcha.jquery.js", "application/x-javascript")]
[assembly: WebResource("Telerik.Sitefinity.Samples.Forms.Resources.Images.bg_QapTcha.png", "image/png")]
[assembly: WebResource("Telerik.Sitefinity.Samples.Forms.Resources.Images.sprites.png", "image/png")]
[assembly: WebResource("Telerik.Sitefinity.Samples.Forms.Resources.CSS.QapTcha.jquery.css", "text/css", PerformSubstitution = true)]

You can now build the code library project and add it to the bin folder of your Sitefinity project.

Registering the QapTcha widget

To be able to use the widget we must do two things:

  1. Register our assembly with the Virtual Path Provider so that Sitefinity can resolve the widget template. To do so go to Administration -> Settings -> Advanced -> VirtualPathSettings -> Virtual paths and add a new virtual path for the control, then restart the website. Sample bellow:
  2. Register the custom control in the Forms toolbox. To so go to Administration -> Settings -> Advanced -> Toolboxes -> FormControls -> Sections -> Common -> Tools and register the custom control.

Download sample project

You can download the project for the custom control from this location: Telerik.Sitefinity.Samples.Forms. Make sure that you resolve the references to Sitefinity and OpenAccess assemblies.

6 comments

Leave a comment
  1. kimbasic Feb 07, 2012
    If you download sample you have to remove the , (comma)  here
    autoRevert : false,};
    in the file  "QapTcha.jquery.js" at least to get it to work in lower versions of IE, it seems like even withit it works in chrome, firefox, safari and opera, thanks for sharing the sample, anyhow there should not be a comma
  2. Adam Apr 19, 2012
    Hi Radoslav,

    I've implemented this but it is easily bypassed by disabling javascript.

    I'm sure I've missed something because that would make it useless for stopping robots. What did I miss?

    Thanks,
    Adam 
  3. Nic Apr 23, 2012
    Hi Radoslav,

    I'm a newbie to Sitefinity, but have been tasked to find a Captcha-style solution to prevent spam on contact forms on a Sitefinity site. The site is version 4.4. This solution you posted looks like it would do what we need, but I am not sure how to go about implementing it. Can you provide any brief steps to get me at least in the right direction?

    Thanks!
    -Nic
  4. Saeed Bidarang Apr 30, 2012
    Thanks for this useful form field.
    We have installed it on our website but it doesn't render the control in IE 7.

    I got following exceptions:

    SCRIPT1028: Expected identifier, string or number 
    Telerik.Web.UI.WebResource.axd?_TSM_HiddenField_=ctl03_TSM&compress=0&_TSM_CombinedScripts_=%3b%3bTelerik.Sitefinity%3aen%3abdddd8ff-c6d7-47b3-8a7f-9a31b79b5edb%3a993d8e92%3a5b182b17%3bTelerik.Sitefinity.Resources%3aen%3a4034d617-0538-40c6-a3aa-bc1ee7517426%3a64f47370%3bTelerik.Sitefinity%2c+Version%3d4.2.1733.0%2c+Culture%3dneutral%2c+PublicKeyToken%3db28c218413bdf563%3aen%3abdddd8ff-c6d7-47b3-8a7f-9a31b79b5edb%3a3b9a1b05%3ae51c0fb3%3bTelerik.Web.UI%2c+Version%3d2011.2.712.40%2c+Culture%3dneutral%2c+PublicKeyToken%3d121fae78165ba3d4%3aen%3adb70bfda-5025-40e2-ae2f-653debb88509%3a16e4e7cd%3af7645509%3a24ee1bba%3ae330518b%3a1e771326%3a8e6f0d33%3bTelerik.Sitefinity%2c+Version%3d4.2.1733.0%2c+Culture%3dneutral%2c+PublicKeyToken%3db28c218413bdf563%3aen%3abdddd8ff-c6d7-47b3-8a7f-9a31b79b5edb%3a1900ec7d%3a2aeb216e%3af77740f1%3a26cfb6dc%3a6e04508f%3bTelerik.Sitefinity.Resources%3aen%3a4034d617-0538-40c6-a3aa-bc1ee7517426%3ac4ef6dcd%3a6f03d72a%3bTelerik.Sitefinity%2c+Version%3d4.2.1733.0%2c+Culture%3dneutral%2c+PublicKeyToken%3db28c218413bdf563%3aen%3abdddd8ff-c6d7-47b3-8a7f-9a31b79b5edb%3a8bc17194%3ac1fc658e%3a447a22b8%3a9790c5c8%3aef84b30a%3bTelerik.Sitefinity.Samples.Forms%2c+Version%3d1.0.0.0%2c+Culture%3dneutral%2c+PublicKeyToken%3dnull%3aen%3a754ba03a-3177-4457-8524-2feabc2398d7%3a1650e3fe%3a388b1581%3ad714d012, line 5262 character 9

  5. Saeed Bidarang May 14, 2012
    Hi Guys,
    The IE7 issue was because of an extra comma in Line 18 of QapTcha.jquery.js file.
  6. Adam May 22, 2012
    Does anybody care that this solution doesn't actually stop robots posting spam?

    Leave a comment