Sunday, January 30, 2005

asp.net custom validator for mmddyy date format

This one is a usability reqt, since system is migrated form mainframe, users have long since got used to enter the date in mmddyy format without any slashes; mainframe CICS screen used to shift to next fld as current fld is filled out. And we needed to have that date textbox to accept date in this format and validate the date. {Personally, i would not prefer handling this and needs to be considered as a training reqt}

To minimize the impact to the code, (almost all times, these reqts spring up only in user acceptance stage, where you have not time think a better solution; will most probably patch the code) i added the following java script, which on tab out of text box, put the slash in approp place, but unfortunately the asp.net validator fires before this onblur event and hence fails, so i had to create a custom validator which formats and also validates the date. I am posting that useless date time patch code below,

<asp:TextBox id="TextBoxDate" runat="server" Width="65" MaxLength="8" CssClass="textbox"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidatorDate" ControlToValidate="TextboxDate" Enabled="false"
Display="Static" ErrorMessage="Date is required and must be in MMDDYY or MM/DD/YY format." Runat="server" />
<asp:CustomValidator id="CustomValidatorDateCheck" runat="server" ErrorMessage="Date or Date format is invalid. Format is MMDDYY or MM/DD/YY." ClientValidationFunction="FormatAndValidateDate" ControlToValidate="TextBoxDate" />

/*
This is having client script for formating and validating date. The following date formats are accepted:
mmddyy, mmddyyyy, mm-dd-yyyy, mm/dd/yyyy, mm.dd.yyyy, mm dd yyyy,
mmm dd yyyy, mmddyyyy, m-d-yyyy, m/d/yyyy, m.d.yyyy,
m d yyyy, mmm d yyyy, m-d-yy, m/d/yy, m.d.yy, m d yy,
mmm d yy (yy is 20yy)
*/
function FormatAndValidateDate(oSource,oArguments) {
var szDate;
var szDateArray;
var szDay;
var szMonth;
var szYear;
var bFound = false;
var aszSeparatorArray = new Array("-"," ","/",".");
var iElementNr;

oArguments.IsValid = false;
if (oArguments.Value.length < 6){
return;
}
szDate = oArguments.Value;

szDate = szDate.replace(/^\s+/,'').replace(/\s+$/,'');

var szDateFormat = new RegExp("^(\\d{1,2})([-./ ]{0,1})(\\d{1,2})\\2((\\d{4})|(\\d{2}))$");
m = szDate.match(szDateFormat);
if (m == null) {
return;
}
for (iElementNr = 0; iElementNr < aszSeparatorArray.length; iElementNr++)
{
if (szDate.indexOf(aszSeparatorArray[iElementNr]) != -1)
{
szDateArray = szDate.split(aszSeparatorArray[iElementNr]);
if (szDateArray.length != 3)
{
return;
}
else
{
szDay = szDateArray[0];
szMonth = szDateArray[1];
szYear = szDateArray[2];
}
bFound = true;
}
}

if (bFound == false)
{
if (szDate.length > 5)
{
szDay = szDate.substr(0, 2);
szMonth = szDate.substr(2, 2);
szYear = szDate.substr(4);
}
}

if(szYear != null)
{
if (szYear.length == 2)
{
szYear = '20' + szYear; //If entered 2 digit yr, consider it as 21st century
}
}

//swap for US date format mm/dd/yy
var szTmp = szDay;
szDay = szMonth;
szMonth = szTmp;

szTmp = "";

if( (szMonth<10) && (szMonth.length == 1))
{
szTmp = "0" + szMonth + "/";
}
else
{
szTmp = szMonth + "/";
}
if( (szDay<10)&& (szDay.length == 1))
{
szTmp += "0" + szDay + "/";
}
else
{
szTmp += szDay + "/";
}

document.getElementById(oSource.controltovalidate).value = szTmp + szYear.substr(2, 2);

szMonth = szMonth - 1; // javascript month range : 0- 11
var tempDate = new Date(szYear,szMonth,szDay);
if ( (typeof(tempDate) == "object") && (getYear(tempDate.getYear()) == szYear) && (szMonth == tempDate.getMonth()) && (szDay == tempDate.getDate()) )
{
oArguments.IsValid = true;
}
}
function getYear(d) {
return (d < 1000) ? d + 1900 : d;
}

Thursday, January 20, 2005

asp.net form submision hijack

I am not sure if anybody in the world is handling this behaviour. To explain the requested behaviour, i am using a simplified scenario, there is a web form which accepts employee number and empolyee name and age. Employee age is validated for range and in code behind employee number is chceked if it's present in database, if not a error message is shown to user using a label control.

In a scenario, user enters a valid age and a employee number which not present in db, so page is processed and label is set with the error message "employee not in db". Now if he enters a valid employee number and invalid age (outside the range) and tabs out, since age range is invalid both client side range validator error and server side error messages are displayed. I was told that user is confused as he knows the employee number is correct but page displays a contradicting message. {my first thoughts is "Oh yeah... so..." } and was asked to remove the message before form is submited.

Initially thought of hijacking the asp.net form submission {for those who like to see how its done, i have included the script } but wrote a client script to clear the label and register using Page.RegisterOnSubmitStatement, since that is fired only when form is submitted, the on-blur of the age control doesn't invoke the script. I didn't had a clue if i can wire client events and when i found the option, i wrote a script to clear the server message.

//Wire the method to events
document.body.attachEvent('onkeydown',ClearServerMessage);
document.body.attachEvent('onmousedown',ClearServerMessage);

// Will hold the Label control client id
var strHTMLElementForServerSideMessage;
function ClearServerMessage(e){
   //get the ServerSideMessage HTML element
   strHTMLElement = document.getElementById(strHTMLElementForServerSideMessage);

   //Clear only when the user does something with validation control elements, such as textbox's,..    if((window.event.srcElement.tagName == "INPUT")
      || (window.event.srcElement.tagName == "SELECT")
      || (window.event.srcElement.tagName == "A") )
   {
      //Check if not null and element of type SPAN, then clear off message
      if( (strHTMLElement != null) && (strHTMLElement.nodeName == 'SPAN') )
      {
         strHTMLElement.innerHTML = "";
      }
      //UnWire the method from events, so it won't be fired again
      document.body.detachEvent('onkeydown',ClearServerMessage);
      document.body.detachEvent('onmousedown',ClearServerMessage);
   }
}

//code behind
string szClrScript = "<script language=JavaScript>strHTMLElementForServerSideMessage=\"" + LabelServerMessage.ClientID + "\";</script>";
if(!Page.IsClientScriptBlockRegistered("szClrSCript"))
Page.RegisterClientScriptBlock("szClrScript", szClrScript);


The fun, form submission hijack script,

<script language="javascript">

// save the original function pointer of the .NET __doPostBack function in a global variable netPostBack
var netPostBack = __doPostBack;

// replace __doPostBack with your own function
__doPostBack = EscapeHtml;

function EscapeHtml (eventTarget, eventArgument)
{
// execute your own code before the page is submitted
document.all." + HtmlText.ClientID + ".value = escape(document.all." + HtmlText.ClientID + ".value);
// call base functionality
return netPostBack (eventTarget, eventArgument);
}

</script>

Friday, January 14, 2005

Iniya pongal nal vazthukal!!!

Happy Pongal 2005


Thanks for all those who keep the divine language from being extinct. Kudos Project Madurai

Thursday, January 13, 2005

repeou nisbum

This prank is really intresting, gist here,

"News from Repeou:
The country Repeou threatens Microsoft to pay a fine as much as 10 percent of its global annual sales for monopoly defenses. The software giant is abusing its monopoly power by bundling several applications such as the Calculator and Paint with Windows.
The process against Microsoft was started by the company named Nisbum. Nisbum developed a great calculator but doesn't see a chance to sell this great product to the masses as long as Microsoft bundles the Calculator with Windows.
According to Repeou, Microsoft must offer at least two separate versions of Windows, one version without the Calculator.
Repeou is giving Microsoft a last opportunity to comment before the case is concluded."


After 24 hrs i could succesfully de-scramble the words "repeou nisbum" -> "europe ibm sun", a good prank on the recent ruling against Microsoft by European Union.

Tuesday, January 11, 2005

MCSD.net...

MCP MCAD MCSD

https://partnering.one.microsoft.com/authenticate/MCPCredentials.aspx

Transcript ID: 690126
Access Code: isasmcsd

70-315 Developing and Implementing Web Applications,
70-316 Developing and Implementing Windows-based Applications,
70-320 Developing XML Web Services and Server Components,
70-300 Analyzing Requirements and Defining .NET Solution Architectures &
70-340 Implementing Security for Applications

After 2 months of preparation and 20+ hrs of travel to/fro Sioux Falls, SD exam center, i am finally certified. Keeping the debate of worthness of the certification apart, the excercise proved to be a good test, to see where i stand in the crowd. With thousands of dumpsters, I am really skeptical of the certificate credits, but at end of the day, knowledge gained is all that matters and i am really satisfied what i gained from this certification excercise.

Monday, January 03, 2005

OpenXML and index scan issue

Faced a peculiar issue with OpenXML, when joining with a table with an OpenXML data, it seems the index being hit is erratic.

CREATE TABLE OpenXMLTest3
(
Col1 INT NOT NULL primary key,
Col2 CHAR(1) NOT NULL,
Col3 CHAR(10)
)
GO

CREATE INDEX IDX_OpenXMLTest3 ON OpenXMLTest3
( Col2, Col3 ) WITH FILLFACTOR = 90 ON [PRIMARY]
GO

Execution plan for the following code gives a index scan on IDX_OpenXMLTest3, but if we load a lot of data into this table, and get the execution plan, it shows a clustered index scan on primary key index. ???? Yes on the "primary key index" ????

DECLARE @hDoc INT
EXEC sp_xml_preparedocument @hDocOUTPUT, '?'

SELECT * FROM OpenXMLTest3 A,
OPENXML (@hDoc, 'ROOT/OpenXMLTest3',1)
WITH (COL2 CHAR(1), COL3 CHAR(10)) B
WHERE A.COL2 = B.COL2 AND
A.COL3 = B.COL3

When we dump the xml data into a table variable and join with that like one below, it was found to give an index seek on IDX_OpenXMLTest3. That is what we used to fix this isse as the table we are joining is a very big invoice table and OpenXML failed hands down.

DECLARE @Temp table (
Col2 CHAR(1) NOT NULL,
Col3 CHAR(10)
)

SELECT * FROM OpenXMLTest3 A, @Temp B
WHERE A.COL2 = B.COL2 AND
A.COL3 = B.COL3