Logo
Lose Weight
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
 
 
Windows Server

Microsoft Content Management Server Development : Validating the HtmlPlaceholderControl (part 3) - Building the Required HTML Placeholder Validator

1/7/2013 3:31:11 PM

3.3 Building the Required HTML Placeholder Validator

We are ready to build the RequiredHtmlPHValidator—a validator that ensures that authors do not leave an HtmlPlaceholderControl empty before the page is saved. To begin, add a class file named RequiredHtmlPHValidator.cs to the MCMSValidators project. Add the following namespaces above the namespace declaration.

using System;
using System.Text;
						using System.Text.RegularExpressions;
						using System.Web.UI;
						using System.Web.UI.WebControls;
						using Microsoft.ContentManagement.WebControls;
						using Microsoft.ContentManagement.Publishing;
						using Microsoft.ContentManagement.Publishing.Extensions.Placeholders;
namespace MCMSValidators
{
  . . . code continues . . .
}

Instead of starting from scratch, our control will inherit the BaseValidator class of the System.Web.UI.WebControls library. BaseValidator already has the basic elements for web control validation such as the ControlToValidate and ErrorMessage properties. This will allow us to implement our control with less code.

. . . code continues . . .
public class RequiredHTMLPHValidator: BaseValidator
{
}
. . . code continues . . .

We don’t have to do anything within the RequiredHTMLPHValidator() constructor, so we will leave that empty.

To implement the RequiredHTMLPHValidator control, we will override the following methods of the BaseValidator class.

Method NameWhat we will program it to do
ControlPropertiesValid()Check to see if the control specified in the ControlToValidate property is an HtmlPlaceholderControl. If it isn’t, the validation control will not be generated when the page loads.
OnPreRender()Inject the client-side JavaScript that checks for empty placeholder controls.
AddAttributesToRender()Pass the name of the client-side JavaScript to be called to the built-in evaluationfunction() method used by all ASP.NET validation controls. Without this step, the script injected in the OnPreRender() method will not be fired when the page is validated.
EvaluateIsValid()We will write logic for performing server-side validation here.

Overriding the ControlPropertiesValid() Method

Let’s start by overriding the ControlPropertiesValid() method of the base class, BaseValidator. The ControlPropertiesValid() method returns a Boolean that indicates whether or not the control to be validated exists and is of the correct object type. In this case, we want it to return true only when the control specified can be found. If the object exists, we proceed to check to see if it is indeed an HtmlPlaceholderControl.

When the control specified does not exist or isn’t a HtmlPlaceholderControl, the ControlPropertiesValid() property returns false and the validation control will not be generated when the page loads.

Add the ControlPropertiesValid() method directly below the RequiredHTMLPHValidator() constructor:

protected override bool ControlPropertiesValid()
{
  Control ctrl = FindControl(ControlToValidate);

  if (ctrl != null)
  {
    return (ctrl is HtmlPlaceholderControl);
  }
  else
  {
    return false;
  }
}

Overriding the OnPreRender() Method

Next, we will make use of the client-side JavaScript that we wrote in the previous section. Being a server-side control, we can’t enter the script as it is. We need to inject it into the page by calling the Page.RegisterClientScript() method. An appropriate place to do so would be in the overridden OnPreRender() method of our custom validator.

Is it possible to put the entire JavaScript in an external *.js file rather than injecting it into the page?

You could put the entire JavaScript into an external *.js file. However, the script is dynamically generated based on the name of the control to validate. For it to work from a static file, you will have to fix the name of the validate() function and modify it to accept the name of the control to validate as an input parameter.

Another compelling reason to inject the script instead of maintaining it in a separate *.js file is to facilitate deployment. By injecting the code, we only need to install the single library file (in our case that’s MCMSValidators.dll) and not worry about the path or the existence of the *.js file as well. In addition, we can put aside any concerns about old cached versions of the file should the script be updated.


Add the OnPreRender() method directly below the ControlPropertiesValid() method:

// register the client-side validation script
protected override void OnPreRender(System.EventArgs e)
{
  base.OnPreRender(e);
  // Add the script only in authoring mode.
  if (WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringNew
   || WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringReedit)
  {
    string phName = "NCPHRICH_";
    if ((FindControl(this.ControlToValidate)) == null
    || !(FindControl(this.ControlToValidate) is HtmlPlaceholderControl))
      return;
    phName += ((HtmlPlaceholderControl)FindControl(this.ControlToValidate) )
              .BoundPlaceholder.Name;
    StringBuilder sb = new StringBuilder();
    sb.Append("<script language=\"javascript\">");
    sb.Append("function validate" + this.ControlToValidate + "()       \n");
    sb.Append("{                                                       \n");
    sb.Append("  var content = document.all." + phName + ".HTML;       \n");
    sb.Append("  if (ReqHtmlPhValid_isEmpty(content))                  \n");
    sb.Append("  {                                                     \n");
    sb.Append("    return false;                                       \n");
    sb.Append("  }                                                     \n");
    sb.Append("  else                                                  \n");
    sb.Append("  {                                                     \n");
    sb.Append("    return true;                                        \n");
    sb.Append("  }                                                     \n");
    sb.Append("}                                                       \n");
    sb.Append("</script>");

    StringBuilder sb2 = new StringBuilder();
    sb2.Append("\n<script language=\"javascript\">                     \n");
    sb2.Append("function ReqHtmlPhValid_isEmpty(content)               \n");
    sb2.Append("{                                                      \n");
    sb2.Append("  // Add more tags to ignore if you need to            \n");
    sb2.Append("  // Here, we ignore <hr> and <img> tags               \n");
    sb2.Append("  // additional tags need to be added and separated    \n");
    sb2.Append("  // with a '|' character.                             \n");
    sb2.Append("  var tagsToKeep = \"img|hr\";                         \n");
    sb2.Append("  // This reg ex matches all <img> and <hr> tags       \n");
    sb2.Append("  var regExpTagsToKeep =                               \n");
    sb2.Append("     \"<[\\\\s|/]*(\" + tagsToKeep + \")\\\\b[^>]*>\"; \n");
    sb2.Append("var reTagsToKeep=new RegExp(regExpTagsToKeep,\"gim\");   \n");
    sb2.Append("  // Check if a tag to keep is included & exit           \n");
    sb2.Append("  if (content.match(reTagsToKeep))                       \n");
    sb2.Append("  {                                                      \n");
    sb2.Append("     // Placeholder is not empty.                        \n");
    sb2.Append("      return false;                                      \n");
    sb2.Append("  }                                                      \n");
    sb2.Append("  // This reg ex gets all tags in the content            \n");
    sb2.Append("  var regExpForAllTags = \"<[^>]*>\";                    \n");
    sb2.Append("  var reAllTags = new RegExp(regExpForAllTags,\"gim\");  \n");
    sb2.Append("  // Remove all Tags by replacing with an empty string   \n");
    sb2.Append("  content = content.replace(reAllTags, \"\");            \n");
    sb2.Append("  // Remove all spaces and non-breaking spaces (&nbsp;)  \n");
    sb2.Append("  content = content.replace(\" \",\"\");                 \n");
    sb2.Append("  content = content.replace(\"&nbsp;\",\"\");            \n");
    sb2.Append("  if (content == \"\")                                   \n");
    sb2.Append("  {                                                      \n");
    sb2.Append("     // All tags removed, leaving an empty string        \n");
    sb2.Append("     // Placeholder is empty.                            \n");
    sb2.Append("     return true;                                        \n");
    sb2.Append("  }                                                      \n");
    sb2.Append("  else                                                   \n");
    sb2.Append("  {                                                      \n");
    sb2.Append("     // After removing all tags, we still have content   \n");
    sb2.Append("     // Placeholder is not empty.                        \n");
    sb2.Append("     return false;                                       \n");
    sb2.Append("  }                                                      \n");
    sb2.Append("}                                                        \n");
    sb2.Append("</script>");

    if (this.RenderUplevel && this.EnableClientScript)
    {
      if (!Page.IsClientScriptBlockRegistered("ReqHtmlPhValid_" + phName
                                           + "_ClientScript"))
      {
        Page.RegisterClientScriptBlock("ReqHtmlPhValid_" + phName +
                                       "_ClientScript", sb.ToString());
      }

      if (!Page.IsClientScriptBlockRegistered("ReqHtmlPhValid_IsEmpty"))
      {
        Page.RegisterClientScriptBlock("ReqHtmlPhValid_IsEmpty ",
                                       sb2.ToString());
      }
    }
  }
}

					  

Notice that we registered the validateNameOfControlToValidate() and ReqHtmlPhValid_isEmpty() methods within two separate client script blocks. By registering the first client script block with a unique key, ReqHtmlPhValid_NameOfPlaceholder_ClientScript, each validation control will have its own version of the validateNameOfControlToValidate() method. The second client script block is registered with a fixed key named ReqHtmlPhValid_IsEmpty. By fixing the key, only one copy of the ReqHtmlPhValid_isEmpty() method will be generated even if there are multiple validation controls on the same page.

Overriding the AddAttributesToRender() Method

Now that we have defined the client-side validateNameOfControlToValidate() method that performs the validation, we need figure out how to trigger it when the page is validated. To do so we will borrow some functionality found in the ASP.NET WebUIValidation.js script. The WebUIValidation.js script file contains methods that support client-side validation, so we can use it to save us re-writing all that functionality from scratch. Before we do so, let’s take a look at how client-side validation works with regular ASP.NET validation controls.

Consider the case of a simple web form that contains several fields, including an ASP.NET button. When the button is clicked, the fields are validated. How does the action of clicking the button trigger validation? For the answer, take a look at the generated HTML code for the button; here’s what you will see:

<input type="submit"
       name="Button1"
      value="Button"
    onclick="if (typeof(Page_ClientValidate)=='function')
Page_ClientValidate();"
   language="javascript"
         id="Button1" />

Notice that embedded within the onclick event of the button, is a call to a method named Page_ClientValidate(). The Page_ClientValidate() function is defined within the WebUIValidation.js file and looks like this:

function Page_ClientValidate()
{
  var i;
  for (i = 0; i < Page_Validators.length; i++) {
    ValidatorValidate(Page_Validators[i]);
  }
  . . . code continues . . .
}

The Page_ClientValidate() method in turn calls the ValidatorValidate() method (also found within the WebUIValidation.js file) for each validation control found on the page. The ValidatorValidate() method performs the crucial step of retrieving the name of the client-side validation routine from an attribute named evaluationfunction (highlighted below) and executes it.

function ValidatorValidate(val)
{
  . . . code continues . . .
  if (typeof(val.evaluationfunction) == "function")
  {
    val.isvalid = val.evaluationfunction(val);
  }
  . . . code continues . . .
}

Therefore, in order to trigger our client-side routine when the page validates, we will have to add an attribute named evaluationfunction to the validation control and assign the name of our function, validateNameOfControlToValidate, as its value.

The good new is that the BaseValidator class has just the method for adding attributes. It’s called AddAttributestoRender(). We will override this method in the base class to insert the evaluationfunction attribute to the control. Add the following code directly below the OnPreRender() method:

// Wiring the client-side javascript to the WebUIValidation.js script file
protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter
writer)
{
  base.AddAttributesToRender(writer);
  if (this.RenderUplevel && this.EnableClientScript)
  {
    // Perform validation only in authoring mode.
    if (WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringNew
     || WebAuthorContext.Current.Mode == WebAuthorContextMode.AuthoringReedit)
    {
      writer.AddAttribute("evaluationfunction", "validate"
                          + this.ControlToValidate);
    }
  }
}

					  

Overriding the EvaluateIsValid() Method

To complete the code, we need to implement the EvaluateIsValid() method of the base class or the project will not compile. This is where we implement server-side checks. For now, we will leave the EvaluateIsValid() empty and program it to always return true. While this isn’t something you should do in a production environment, don’t worry. We’ll be filling this method out later to provide server-side validation in the section Implementing Server-Side Validation. Add the EvaluateIsValid() method directly below the AddAttributesToRender() method.

protected override bool EvaluateIsValid()
{
  bool valid = true;
  return valid;
}

The custom validation control is now complete. Save and compile the solution.

3.4 Adding the Custom Validator to the Template File

Before using our custom control, we first need to copy it to the bin directory of our project file or add it as a reference to the MCMS web application project. After you have done that, add the control to the Visual Studio .NET Toolbox by choosing the Add/Remove Items option (or Customize Toolbox option for VS.NET 2002). You can then drag and drop it onto template files. For the control to work properly, you will have to set the ControlToValidate property to contain the value of the ID of the HtmlPlaceholderControl that you are validating and the ErrorMessage property to contain a friendly error message for the author.

The ID of the HtmlPlaceholderControl does not appear in the ControlToValidate dropdown list. Shouldn’t the dropdown list be populated with placeholder IDs that match the type of control being validated?

In order for the ID of the control to be included in the dropdown, we have to make the HtmlPlaceholderControl compatible with the ASP.NET validation control architecture. We will do just this in the section, The HtmlPlaceholderControl with Validation.

Other -----------------
- Windows Server 2003 on HP ProLiant Servers : Migration Case Studies (part 3) - Hewlett-Packard Company
- Windows Server 2003 on HP ProLiant Servers : Migration Case Studies (part 2) - Eastman Chemical Company
- Windows Server 2003 on HP ProLiant Servers : Migration Case Studies (part 1) - County Government Office
- System Center Configuration Manager 2007 : Network Design - Troubleshooting Configuration Manager Network Issues (part 2) - Identifying Network Issues Affecting Configuration Manager
- System Center Configuration Manager 2007 : Network Design - Troubleshooting Configuration Manager Network Issues (part 1)
- System Center Configuration Manager 2007 : Network Design - Network Discovery
- Exchange Server 2007 : Deploying a Cluster Continuous Replication Mailbox Cluster (part 2)
- Exchange Server 2007 : Deploying a Cluster Continuous Replication Mailbox Cluster (part 1)
- Microsoft Dynamic AX 2009 : Report Customization (part 2) - Adding Promotional Materials to an Invoice Report
- Microsoft Dynamic AX 2009 : Report Customization (part 1) - Creating Promotional Materials
- Leveraging the SharePoint Workspace : Edit a List Item Using the Edit Form Offline, Create a New List Item Using the New Form Offline, Synchronize Offline Changes to SharePoint
- Leveraging the SharePoint Workspace : Leveraging the SharePoint Workspace, View Your List and Display Form Offline
- BizTalk Server 2009 Operations : Disaster Recovery (part 2)
- BizTalk Server 2009 Operations : Disaster Recovery (part 1) - Configuring the Destination System for Log Shipping
- Windows Server 2008 : Promoting a Domain Controller with dcpromo
- Windows Server 2008 : Retrieving Information About Objects with dsget, Viewing and Modifying AD Permissions with dsacls
- Microsoft Systems Management Server 2003 : NTFS Security
- Microsoft Systems Management Server 2003 : Standard and Advanced Security
- System Center Configuration Manager 2007 : Network Design - Use of BITS
- System Center Configuration Manager 2007 : Network Design - Fast Networks and Slow Networks
 
 
Popular tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS
Top 10
- Windows Phone 8 Apps : Camera (part 4) - Adjusting Video Settings, Using the Video Light
- Windows Phone 8 Apps : Camera (part 3) - Using the Front Camera, Activating Video Mode
- Windows Phone 8 Apps : Camera (part 2) - Controlling the Camera’s Flash, Changing the Camera’s Behavior with Lenses
- Windows Phone 8 Apps : Camera (part 1) - Adjusting Photo Settings
- MDT's Client Wizard : Package Properties
- MDT's Client Wizard : Driver Properties
- MDT's Client Wizard : Application Properties
- MDT's Client Wizard : Operating System Properties
- MDT's Client Wizard : Customizing the Deployment Share
- Windows Server 2012 : Software and User Account Control Administration (part 5) - Maintaining application integrity - Configuring run levels
 
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
2015 Camaro