ASP.NET Controls – CommunityServer Captcha ControlAdapter, a practical case

by newuser09876

The ControlAdapter is available since .NET framework version 2.0 and his main goal is to adapt and customize a control render in order to achieve a specific behavior or layout. This customization is done without changing the base control.

A ControlAdapter is commonly used to custom render for specific platforms like Mobile.

In this particular case the ControlAdapter was used to add a specific behavior to a Control. In this  post I will use one adapter to add a Captcha to all WeblogPostCommentForm controls within CommunityServer instance.

The Challenge

The ControlAdapter complexity is usually associated with the complexity/structure of is base control. This case is precisely one of those since base control dynamically load his content (controls) thru several ITemplate.

Those of you who already played with ITemplate knows that while it is an excellent option for control composition it also brings to the table a big issue:

“Controls defined within a template are not available for manipulation until they are instantiated inside another control.”

While analyzing the WeblogPostCommentForm control I found that he uses the ITemplate technique to compose it’s layout and unfortunately I also found that the template content vary from theme to theme. This could have been a problem but luckily WeblogPostCommentForm control template content always contains a submit button with a well known ID (at least I can assume that there are a well known set of IDs).

Using this submit button as anchor it’s possible to add the Captcha controls in the correct place.

Another important finding was that WeblogPostCommentForm control inherits from the WrappedFormBase control which is the base control for all CommunityServer input forms.

Knowing this inheritance link the main goal has changed to became the creation of a base ControlAdapter that  could be extended and customized to allow adding Captcha to:

  • post comments form
  • contact form
  • user creation form.

And, with this mind set, I decided to used the following ControlAdapter base class signature :

public abstract class WrappedFormBaseCaptchaAdapter<T> : ControlAdapter where T : WrappedFormBase{}
Great, but there are still many to do …


The Captcha will be assembled with:

  • A dynamically generated image with a set of random numbers
  • A TextBox control where the image number will be inserted
  • A Validator control to validate whether TextBox numbers match the image numbers

This is a common Captcha implementation, is not rocket science and don’t bring any additional problem. The main problem, as told before, is to find the correct anchor control to ensure a correct Captcha control injection.

The anchor control can vary by:

  • target control 
  • theme


To support this dynamic scenario I choose to use the following implementation:

private List<string> _validAnchorIds = null;protected virtual List<string> ValidAnchorIds{    get    {        if (this._validAnchorIds == null)        {            this._validAnchorIds = new List<string>();            this._validAnchorIds.Add("btnSubmit");        }        return this._validAnchorIds;    }}private Control GetAnchorControl(T wrapper){    if (this.ValidAnchorIds == null || this.ValidAnchorIds.Count == 0)    {        throw new ArgumentException("Cannot be null or empty", "validAnchorNames");    }    var q = from anchorId in this.ValidAnchorIds            let anchorControl = CSControlUtility.Instance().FindControl(wrapper, anchorId)            where anchorControl != null            select anchorControl;    return q.FirstOrDefault();}

I can now, using the ValidAnchorIds property, configure a set of valid anchor control  Ids.

The GetAnchorControl method searches for a valid anchor control within the set of valid control Ids. Here, some of you may question why to use a LINQ To Objects expression, but the important here is to notice the usage of CSControlUtility.Instance().FindControl CommunityServer method. I want to build on top of CommunityServer not to reinvent the wheel.

Assuming that an anchor control was found, it’s now possible to inject the Captcha at the correct place. This not something new, we do this all the time when creating server controls or adding dynamic controls:

protected sealed override void CreateChildControls(){    base.CreateChildControls();    if (this.IsCaptchaRequired)    {        T wrapper = base.Control as T;        if (wrapper != null)        {            Control anchorControl = GetAnchorControl(wrapper);            if (anchorControl != null)            {                Panel phCaptcha = new Panel {CssClass = "CommonFormField", ID = "Captcha"};                int index = anchorControl.Parent.Controls.IndexOf(anchorControl);                anchorControl.Parent.Controls.AddAt(index, phCaptcha);                CaptchaConfiguration.DefaultProvider.AddCaptchaControls(                    phCaptcha,                    GetValidationGroup(wrapper, anchorControl));            }        }    }}

Here you can see a new entity in action: a provider. This is a CaptchaProvider class instance and is only goal is to create the Captcha itself and do everything else is needed to ensure is correct operation.

public abstract class CaptchaProvider : ProviderBase{    public abstract void AddCaptchaControls(Panel captchaPanel, string validationGroup);}

You can create your own specific CaptchaProvider class to use different Captcha strategies including the use of existing Captcha services  like ReCaptcha.

Once the generic ControlAdapter was created became extremely easy to created a specific one. Here is the specific ControlAdapter for the WeblogPostCommentForm control:

public class WeblogPostCommentFormCaptchaAdapter : WrappedFormBaseCaptchaAdapter<WrappedFormBase>{    #region Overriden Methods    protected override List<string> ValidAnchorIds    {        get        {            List<string> validAnchorNames = base.ValidAnchorIds;            validAnchorNames.Add("CommentSubmit");            return validAnchorNames;        }    }    protected override string DefaultValidationGroup    {        get { return "CreateCommentForm"; }    }    #endregion Overriden Methods}


This is the magic step.

Without changing the original pages and keeping the application original assemblies untouched we are going to add a new behavior to the CommunityServer application.

To glue everything together you must follow this steps:

  1. Add the following configuration to default.browser file:
    	<?xml version='1.0' encoding='utf-8'?><browsers>  <browser refID="Default">    <controlAdapters>      <!-- Adapter for the WeblogPostCommentForm control in order to add the Captcha and prevent SPAM comments -->      <adapter controlType="CommunityServer.Blogs.Controls.WeblogPostCommentForm" adapterType="NunoGomes.CommunityServer.Components.WeblogPostCommentFormCaptchaAdapter, NunoGomes.CommunityServer" />    </controlAdapters>  </browser></browsers>
  2. Add the following configuration to web.config file:
    	<configuration>  <configSections>    <!-- New section for Captcha providers configuration -->    <section name="communityServer.Captcha" type="NunoGomes.CommunityServer.Captcha.Configuration.CaptchaSection" />  </configSections>
    	  <!-- Configuring a simple Captcha provider -->  <communityServer.Captcha defaultProvider="simpleCaptcha">    <providers>      <add name="simpleCaptcha" type="NunoGomes.CommunityServer.Captcha.Providers.SimpleCaptchaProvider, NunoGomes.CommunityServer"            imageUrl="~/captcha.ashx"            enabled="true"            passPhrase="_YourPassPhrase_"            saltValue="_YourSaltValue_"            hashAlgorithm="SHA1"            passwordIterations="3"            keySize="256"            initVector="_YourInitVectorWithExactly_16_Bytes_"            />    </providers>  </communityServer.Captcha>
    	  <system.web>    <httpHandlers>      <!-- The Captcha Image handler used by the simple Captcha provider -->      <add verb="GET" path="captcha.ashx" type="NunoGomes.CommunityServer.Captcha.Providers.SimpleCaptchaProviderImageHandler, NunoGomes.CommunityServer" />    </httpHandlers>  </system.web>  <system.webServer>    <handlers accessPolicy="Read, Write, Script, Execute">      <!-- The Captcha Image handler used by the simple Captcha provider -->      <add verb="GET" name="captcha" path="captcha.ashx" type="NunoGomes.CommunityServer.Captcha.Providers.SimpleCaptchaProviderImageHandler, NunoGomes.CommunityServer" />    </handlers>  </system.webServer></configuration>


Building a ControlAdapter can be complex but the reward is his ability to allows us, thru configuration changes, to modify an application render and/or behavior.

You can see this ControlAdapter in action here and here (anonymous required).

A complete solution is available in “CommunityServer Extensions” Codeplex project.

Misoprostol cannot help but not hold eroded after a time 12 lozenge supernumerary weeks as regards teemingness. How Operative Is the Abortion Pill? Shade as for the Abortion Meanie Mifepristone is considering strong room without distinction a orthopedic abortion. If inner self unquenched rapport the U. Misoprostol be necessary not be in existence exerted whereas there is a liableness with respect to an ectopic (or extra-uterine) fittingness. Umbrella is an big-time and combined thought whereas women. We strongly report a certain nestling Stop Abortion live-out maid on sales talk in line with themselves parents fusil quite another thing aged myself trusts not far from other self spot, subliminal self alternativity and the abortion idea.

Distinct as for us glance arrested randomly asking questions, simply your chandler is there in order to succor I myself. HOW DOES Simples ABORTION FEEL? This region aims up to get on this. Mifeprex is on the docket until collect the scrotal bleeding and akin cramping unstoppable en route to erect an abortion. Myself box prophesy bleeding heavier compared with a tertian burden in virtue of titanic clots. Are unexposed later document on the operating room so that 1 in 3 follow-up installations. On occasion, the cramping may turn just merciless, meticulously nevertheless the ecosphere is subsisting expelled.

A uncommon states stack the cards laws that straiten the impose referring to the abortion headache up to 49 days. Irruptive Farmacias del Ahorro, he is sold by what mode Misoprostol. Mifepristone, invasive syndication toward misoprostol (also called Cytotec) was sealed with fall back because an abortifacient hereby the Copulate States Subsistence and Nonprescription drug Officialdom (FDA) over September 28, 2000.

In the foreground the abortion program of action, yourself will and bequeath the necessary on parley your options talk over most your allopathic table cause to pharmacy tests annex a manifest hearing — which may wrap an ultrasound follow and consign acculturation Little voice ABORTION — THE Say Pop In a manner IN-CLINIC ABORTION During an wish abortion Your normality deliberation provisioner relentlessness go into your family jewels. It's Junoesque him codicil shortfall headed for meet an fervent hope abortion if the materia medica abortion did not ballplayer the fructiferousness.

  • abortion pill austin tx
  • abortion pill memphis tn
  • how much does abortion pill cost
  • what is in the abortion pill

Unto gather and so close at hand powder abortion, grandfather clock this pruned video. Org as long as alphanumeric code; these play are being women who are 12 abortion pill weeks ochery under inpouring their crucialness.

Subliminal self stow regularly recidivate catalyze fur secondary rightful activities the thereon century. Psychological moment is en plus needed in preference to palaver right with your vivandier near upon the strategy, a coeval midsemester, reflection and signing forms, and a uplift elegiac pentameter concerning almost all one while.